ERC1155StrategyFactory

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/factories/ERC1155StrategyFactory.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import {ERC1155Strategy} from "../strategies/ERC1155Strategy.sol";
import {INFTStrategyHook, INFTStrategyFactory} from "../Interfaces.sol";
import {LibClone} from "solady/utils/LibClone.sol";
import {StrategyFactory} from "./StrategyFactory.sol";
import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol";

/// @title ERC1155StrategyFactory - Factory for deploying ERC1155Strategy contracts
/// @author TokenWorks (https://token.works/)
/// @notice This factory deploys and manages ERC1155Strategy contracts with Uniswap V4 integration
/// @dev Uses ERC1967 proxy pattern for upgradeable ERC1155Strategy deployments
contract ERC1155StrategyFactory is StrategyFactory {
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™                ™™™™™™™™™™™                ™™™™™™™™™™™ */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™               ™™™™™™™™™™™™               ™™™™™™™™™™  */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™              ™™™™™™™™™™™™™              ™™™™™™™™™™™  */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™             ™™™™™™™™™™™™™™            ™™™™™™™™™™™   */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™            ™™™™™™™™™™™™™™™            ™™™™™™™™™™™   */
    /*                ™™™™™™™™™™™            ™™™™™™™™™™™           ™™™™™™™™™™™™™™™           ™™™™™™™™™™™    */
    /*                ™™™™™™™™™™™             ™™™™™™™™™™          ™™™™™™™™™™™™™™™™™          ™™™™™™™™™™™    */
    /*                ™™™™™™™™™™™             ™™™™™™™™™™          ™™™™™™™™™™™™™™™™™          ™™™™™™™™™™     */
    /*                ™™™™™™™™™™™              ™™™™™™™™™™        ™™™™™™™™™™™™™™™™™™™        ™™™™™™™™™™™     */
    /*                ™™™™™™™™™™™              ™™™™™™™™™™™       ™™™™™™™™™ ™™™™™™™™™       ™™™™™™™™™™™      */
    /*                ™™™™™™™™™™™               ™™™™™™™™™™      ™™™™™™™™™™ ™™™™™™™™™™      ™™™™™™™™™™™      */
    /*                ™™™™™™™™™™™               ™™™™™™™™™™      ™™™™™™™™™   ™™™™™™™™™      ™™™™™™™™™™       */
    /*                ™™™™™™™™™™™                ™™™™™™™™™™    ™™™™™™™™™™    ™™™™™™™™™    ™™™™™™™™™™        */
    /*                ™™™™™™™™™™™                 ™™™™™™™™™™   ™™™™™™™™™     ™™™™™™™™™™  ™™™™™™™™™™™        */
    /*                ™™™™™™™™™™™                 ™™™™™™™™™™  ™™™™™™™™™™     ™™™™™™™™™™  ™™™™™™™™™™         */
    /*                ™™™™™™™™™™™                  ™™™™™™™™™™™™™™™™™™™™       ™™™™™™™™™™™™™™™™™™™™          */
    /*                ™™™™™™™™™™™                   ™™™™™™™™™™™™™™™™™™         ™™™™™™™™™™™™™™™™™™           */
    /*                ™™™™™™™™™™™                   ™™™™™™™™™™™™™™™™™™         ™™™™™™™™™™™™™™™™™™           */
    /*                ™™™™™™™™™™™                    ™™™™™™™™™™™™™™™™           ™™™™™™™™™™™™™™™™            */
    /*                ™™™™™™™™™™™                     ™™™™™™™™™™™™™™             ™™™™™™™™™™™™™™             */
    /*                ™™™™™™™™™™™                     ™™™™™™™™™™™™™™             ™™™™™™™™™™™™™™             */
    /*                ™™™™™™™™™™™                      ™™™™™™™™™™™™               ™™™™™™™™™™™™              */

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                      CONSTANTS                      */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                   STATE VARIABLES                   */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice Mapping of NFT collection addresses to their ERC1155Strategy contracts
    mapping(bytes32 => address) private _keyToStrategy;
    /// @notice Mapping of ERC1155Strategy addresses to their collection contracts
    mapping(address => bytes32) private _strategyToKey;

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                    CUSTOM ERRORS                    */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice Collection already has an ERC1155Strategy deployed
    error CollectionAlreadyLaunched();
    /// @notice Incorrect ETH amount sent with launch transaction
    error WrongEthAmount();
    /// @notice Contract does not implement ERC721 interface
    error NotERC1155();
    /// @notice Buy increment is outside valid range
    error InvalidIncrement();

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                    CUSTOM EVENTS                    */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice Event emitted when a new ERC1155Strategy instance is launched
    /// @param collection The NFT collection address
    /// @param strategy The deployed ERC1155Strategy contract address
    /// @param key The strategy uniq "key"
    /// @param ids The strategy ids
    /// @param tokenName The name of the strategy token
    /// @param tokenSymbol The symbol of the strategy token
    event ERC1155StrategyLaunched(
        address indexed collection,
        address indexed strategy,
        bytes32 key,
        uint256[] ids,
        string tokenName,
        string tokenSymbol
    );

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                     CONSTRUCTOR                     */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice Constructor initializes the factory with required dependencies
    /// @param implementation the ERC1155Strategy implementation
    /// @param _posm Uniswap V4 Position Manager address
    /// @param _permit2 Permit2 contract address
    /// @param _router Uniswap V4 Router address
    /// @param _poolManager Uniswap V4 Pool Manager address
    /// @param _feeAddress Address to receive deployment fees
    /// @dev Sets up immutable references and creates ERC1155Strategy implementation
    constructor(
        address implementation,
        address _posm,
        address _permit2,
        address payable _router,
        address _poolManager,
        address _feeAddress
    ) StrategyFactory(_posm, _permit2, _router, _poolManager, _feeAddress) {
        strategyImplementation = implementation;
    }

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                    USER FUNCTIONS                   */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice Launches a new ERC1155Strategy contract with owner permissions
    /// @param collection Address of the NFT collection contract
    /// @param ids ids for the ERC1155Strategy
    /// @param tokenName Name of the strategy token
    /// @param tokenSymbol Symbol of the strategy token
    /// @param collectionOwner Address that will receive fees from the strategy
    /// @param buyIncrement The buy increment for price calculations
    /// @return The deployed ERC1155Strategy contract
    /// @dev Only callable by contract owner, deploys ERC1155Strategy and initializes liquidity
    function ownerLaunchStrategy(
        address collection,
        ERC1155Strategy.StrategyType strategyType,
        uint256[] memory ids,
        string memory tokenName,
        string memory tokenSymbol,
        address collectionOwner,
        uint256 buyIncrement
    ) external payable onlyLauncher returns (ERC1155Strategy) {
        // Validate the parameters passed
        if (hookAddress == address(0)) {
            revert HookNotSet();
        }

        if (checkIfAlreadyLaunched(collection, strategyType, ids)) {
            revert CollectionAlreadyLaunched();
        }

        if (buyIncrement < 0.01 ether || buyIncrement > 0.1 ether) {
            revert InvalidIncrement();
        }

        if (!IERC165(collection).supportsInterface(0xd9b67a26)) {
            revert NotERC1155();
        }

        ERC1155Strategy strategy = _deployUpgradeableNFTStrategy(
            collection,
            strategyType,
            ids,
            hookAddress,
            tokenName,
            tokenSymbol,
            buyIncrement,
            owner()
        );

        bytes32 key = getKey(collection, strategyType, ids);

        _keyToStrategy[key] = address(strategy);
        _strategyToKey[address(strategy)] = key;

        // Costs 2 wei
        _loadLiquidity(address(strategy));

        // Set fees to collectionOwner
        INFTStrategyHook(hookAddress).adminUpdateFeeAddress(
            address(strategy),
            collectionOwner
        );

        emit ERC1155StrategyLaunched(
            collection,
            address(strategy),
            key,
            ids,
            tokenName,
            tokenSymbol
        );

        return strategy;
    }

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                  INTERNAL FUNCTIONS                 */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice Deploys a new upgradeable ERC1155Strategy contract
    /// @param _collection The NFT collection address
    /// @param strategyType The strategy type for ERC1155Strategy
    /// @param _ids The ids for ethe ERC1155Strategy
    /// @param _hook The hook contract address
    /// @param _tokenName The name of the strategy token
    /// @param _tokenSymbol The symbol of the strategy token
    /// @param _buyIncrement The buy increment for price calculations
    /// @param _owner The owner of the ERC1155Strategy contract
    /// @return proxy The deployed ERC1155Strategy proxy contract
    /// @dev Uses ERC1967 proxy pattern with packed immutable args
    function _deployUpgradeableNFTStrategy(
        address _collection,
        ERC1155Strategy.StrategyType strategyType,
        uint256[] memory _ids,
        address _hook,
        string memory _tokenName,
        string memory _tokenSymbol,
        uint256 _buyIncrement,
        address _owner
    ) internal returns (ERC1155Strategy proxy) {
        bytes memory args = abi.encodePacked(
            address(this),
            router,
            poolManager
        );

        proxy = ERC1155Strategy(
            payable(
                LibClone.deployERC1967(address(strategyImplementation), args)
            )
        );

        proxy.initialize(
            _collection,
            strategyType,
            _ids,
            _hook,
            _tokenName,
            _tokenSymbol,
            _buyIncrement,
            launchUpgradeable ? _owner : address(0)
        );
    }

    /// @param collection The address of the NFT collection to check
    /// @return True if collection already has a strategy, false otherwise
    /// @dev Checks both current factory and old factory for existing strategies
    function checkIfAlreadyLaunched(
        address collection,
        ERC1155Strategy.StrategyType strategyType,
        uint256[] memory ids
    ) public view returns (bool) {
        bytes32 key = getKey(collection, strategyType, ids);

        if (_keyToStrategy[key] != address(0)) {
            return true;
        }

        return false;
    }

    function getKey(
        address collection,
        ERC1155Strategy.StrategyType strategyType,
        uint256[] memory ids
    ) public pure returns (bytes32) {
        return keccak256(abi.encode(collection, strategyType, ids));
    }
}
"
    },
    "src/strategies/ERC1155Strategy.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import {ERC1155} from "solady/tokens/ERC1155.sol";
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";

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

/// @title ERC1155Strategy - An ERC20 token that constantly churns NFTs from a collection
/// @author TokenWorks (https://token.works/)
/// @notice This contract implements an ERC20 token backed by another token.
///         Users can trade the token on Uniswap V4, and the contract uses trading fees to buy bags of the underlying token.
/// @dev Uses ERC1967 proxy pattern with immutable args for gas-efficient upgrades
contract ERC1155Strategy is BaseStrategy {
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™                ™™™™™™™™™™™                ™™™™™™™™™™™ */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™               ™™™™™™™™™™™™™              ™™™™™™™™™™  */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™              ™™™™™™™™™™™™™              ™™™™™™™™™™™  */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™             ™™™™™™™™™™™™™™            ™™™™™™™™™™™   */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™            ™™™™™™™™™™™™™™™            ™™™™™™™™™™™   */
    /*                ™™™™™™™™™™™            ™™™™™™™™™™™           ™™™™™™™™™™™™™™™           ™™™™™™™™™™™    */
    /*                ™™™™™™™™™™™             ™™™™™™™™™™          ™™™™™™™™™™™™™™™™™          ™™™™™™™™™™™    */
    /*                ™™™™™™™™™™™             ™™™™™™™™™™          ™™™™™™™™™™™™™™™™™          ™™™™™™™™™™     */
    /*                ™™™™™™™™™™™              ™™™™™™™™™™        ™™™™™™™™™™™™™™™™™™™        ™™™™™™™™™™™     */
    /*                ™™™™™™™™™™™              ™™™™™™™™™™™       ™™™™™™™™™ ™™™™™™™™™       ™™™™™™™™™™™      */
    /*                ™™™™™™™™™™™               ™™™™™™™™™™      ™™™™™™™™™™ ™™™™™™™™™™      ™™™™™™™™™™™      */
    /*                ™™™™™™™™™™™               ™™™™™™™™™™      ™™™™™™™™™   ™™™™™™™™™      ™™™™™™™™™™       */
    /*                ™™™™™™™™™™™                ™™™™™™™™™™    ™™™™™™™™™™    ™™™™™™™™™    ™™™™™™™™™™        */
    /*                ™™™™™™™™™™™                 ™™™™™™™™™™   ™™™™™™™™™     ™™™™™™™™™™  ™™™™™™™™™™™        */
    /*                ™™™™™™™™™™™                 ™™™™™™™™™™  ™™™™™™™™™™     ™™™™™™™™™™  ™™™™™™™™™™         */
    /*                ™™™™™™™™™™™                  ™™™™™™™™™™™™™™™™™™™™       ™™™™™™™™™™™™™™™™™™™™          */
    /*                ™™™™™™™™™™™                   ™™™™™™™™™™™™™™™™™™         ™™™™™™™™™™™™™™™™™™           */
    /*                ™™™™™™™™™™™                   ™™™™™™™™™™™™™™™™™™         ™™™™™™™™™™™™™™™™™™           */
    /*                ™™™™™™™™™™™                    ™™™™™™™™™™™™™™™™           ™™™™™™™™™™™™™™™™            */
    /*                ™™™™™™™™™™™                     ™™™™™™™™™™™™™™             ™™™™™™™™™™™™™™             */
    /*                ™™™™™™™™™™™                     ™™™™™™™™™™™™™™             ™™™™™™™™™™™™™™             */
    /*                ™™™™™™™™™™™                      ™™™™™™™™™™™™               ™™™™™™™™™™™™              */

    struct StrategyRange {
        uint256 start;
        uint256 end;
    }

    enum StrategyType {
        Range,
        List,
        All
    }

    struct Listing {
        uint256 price;
        uint256 tokenId;
    }

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                     CONSTANTS                       */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice The collection this strategy is tied to
    ERC1155 public collection;

    /// @notice the range of ids, only if strategyType == StrategyType.Range
    StrategyRange public range;

    /// @notice the list of ids this strategy focuses on, only if strategyType == StrategyType.List
    mapping(uint256 => bool) acceptedIds;

    /// @notice the strategy type
    StrategyType public strategyType;

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                   STATE VARIABLES                   */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice Mapping of listingId => Listing
    mapping(uint256 => Listing) public listings;

    /// @notice Counter for listings
    uint256 lastListingId;

    /// @notice Storage gap for future upgrades (prevents storage collisions)
    uint256[50] private __gap;

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                   CUSTOM EVENTS                     */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice Emitted when the protocol buys from the collection
    event ERC1155BoughtByProtocol(
        uint256 indexed listingId,
        uint256 indexed tokenId,
        uint256 purchasePrice,
        uint256 listPrice
    );

    /// @notice Emitted when the protocol buys from the collection
    event ERC1155SoldByProtocol(
        uint256 indexed listingId,
        uint256 indexed tokenId,
        uint256 price,
        address buyer
    );

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                    CUSTOM ERRORS                    */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice given sale id is not currently for sale
    error NotForSale();
    /// @notice Sent ETH amount is less than the bag sale price
    error PriceTooLow();
    /// @notice Call didn't result in buying the right amount of the token
    error NeedToBuyNFT();
    /// @notice triggered when trying to buy tokens for 0
    error NoZeroBuys();
    /// @notice triggered when there is an error in inputs
    error InputsError();
    /// @notice triggered when target is collection
    error InvalidTarget();
    /// @notice triggered when trying to buy a token not in the list (or range)
    error InvalidTokenId();
    /// @notice triggered when the external call fails when buying an NFT
    error ExternalCallFailed(bytes reason);
    /// @notice triggered when the given ids are not in the right order
    error InvalidIds();
    /// @notice triggered when receiving ERC1155 items from a wrong collection
    error InvalidCollection();
    /// @notice triggered when receiving too many items
    error InvalidAmount();

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                    CONSTRUCTOR                      */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice Constructor calls BaseStrategy() to disable initializers
    /// @dev This is required for the proxy pattern to work correctly
    constructor() BaseStrategy() {}

    /// @notice Initializes the contract with required addresses and permissions
    /// @param _collection Address of the underlying ERC20 contract
    /// @param _strategyType type of ERC1155Strategy: Range|List
    /// @param _ids ids to initialize the type (list must be in ascending order)
    /// @param _hook Address of the StrategyHook contract
    /// @param _tokenName Name of the token
    /// @param _tokenSymbol Symbol of the token
    /// @param _buyIncrement Buy increment for the token
    /// @param _owner Owner of the contract
    function initialize(
        address _collection,
        StrategyType _strategyType,
        uint256[] memory _ids,
        address _hook,
        string memory _tokenName,
        string memory _tokenSymbol,
        uint256 _buyIncrement,
        address _owner
    ) external initializer {
        require(_collection != address(0), "Invalid collection");

        collection = ERC1155(_collection);
        strategyType = _strategyType;

        _initializeIds(_strategyType, _ids);

        __BaseStrategy_init(
            _hook,
            _tokenName,
            _tokenSymbol,
            _buyIncrement,
            _owner
        );
    }

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                 GETTERS                             */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @dev this must be incremented whenever there is any change in BaseStrategy or this strategy
    function VERSION() public pure override returns (uint256) {
        return 1;
    }

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                 MECHANISM FUNCTIONS                 */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice Buys a specific NFT using accumulated fees
    /// @param value Amount of ETH to spend on the NFT purchase
    /// @param data Calldata for the external marketplace call
    /// @param tokenId The token ID expected to be acquired
    /// @param target The marketplace contract to call
    /// @dev Protected against reentrancy, validates NFT acquisition
    function buyTargetNFT(
        uint256 value,
        bytes calldata data,
        uint256 tokenId,
        address target
    ) external nonReentrant {
        if (target == address(collection)) revert InvalidTarget();

        // here we do not check the tokenId validity
        // it will be checked in onERC1155Received since it's always called
        // for ERC1155 collections.
        // checking in onERC1155Received ensures only wanted ids of that collection are accepted

        uint256 ethBalanceBefore = address(this).balance;
        uint256 funds = availableFunds();

        // this prevents tokens from being locked in contract because if salePrice == 0
        // tokens would be seen as not on sale
        if (funds == 0) {
            revert NoZeroBuys();
        }

        // ensure the users doesn't ask for too much value
        if (value > funds) {
            revert NotEnoughEth();
        }

        uint256 listingId = (++lastListingId);

        uint256 tokenBalanceBefore = collection.balanceOf(
            address(this),
            tokenId
        );

        // Call external
        (bool success, bytes memory reason) = target.call{value: value}(data);
        if (!success) {
            revert ExternalCallFailed(reason);
        }

        if (
            collection.balanceOf(address(this), tokenId) !=
            tokenBalanceBefore + 1
        ) {
            revert NeedToBuyNFT();
        }

        // Calculate actual cost of the NFT to base new price on
        uint256 cost = ethBalanceBefore - address(this).balance;
        currentFees -= cost;

        uint256 listPrice = (cost * priceMultiplier) / 1000;
        listings[listingId] = Listing({price: listPrice, tokenId: tokenId});

        // Update last buy block to reset max price calculation
        lastBuyBlock = block.number;

        emit ERC1155BoughtByProtocol(listingId, tokenId, cost, listPrice);
    }

    /// @notice Sell an NFT that was previously bought and listed
    /// @param listingId The ID of the bag to sell
    function sellListing(uint256 listingId) external payable nonReentrant {
        Listing memory listing = listings[listingId];

        uint256 salePrice = listing.price;
        uint256 tokenId = listing.tokenId;

        // Verify listing is for sale
        if (salePrice == 0) revert NotForSale();

        // Verify sent ETH matches sale price
        if (msg.value != salePrice) revert PriceTooLow();

        // Remove from listings
        delete listings[listingId];

        // Transfer token to buyer
        collection.safeTransferFrom(address(this), msg.sender, tokenId, 1, "");

        // Add sale price to fees
        ethToTwap += salePrice;

        emit ERC1155SoldByProtocol(listingId, tokenId, salePrice, msg.sender);
    }

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                  GETTER FUNCTIONS                   */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice Returns all listings from id 0 to {lastListingId} included
    /// @dev switch to list(start, end) if this function ever revert because too much gas
    /// @return all listing from id 0 to {lastListingId}
    function list() external view returns (Listing[] memory) {
        return list(0, lastListingId);
    }

    /// @notice Returns all listings from id {startId} to {endId} included
    /// @param startId the id of the first bag
    /// @param endId the id of the last bag
    /// @return _listings all listings from id {startId} to {endId}
    function list(
        uint256 startId,
        uint256 endId
    ) public view returns (Listing[] memory _listings) {
        if (endId < startId) {
            revert InputsError();
        }

        uint256 length = endId - startId + 1;
        _listings = new Listing[](length);

        for (uint256 i; i < length; i++) {
            _listings[i] = listings[startId + i];
        }
    }

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                  Internal FUNCTIONS                 */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @dev initialize the strategy with type and ids
    /// @dev if type == Range, ids.length must be 2, in ascending order
    /// @dev if type == List, any number of ids, in ascending order
    /// @dev if type == All, ids must be empty
    /// @param _strategyType the strategy type (Range|List|All)
    /// @param ids the list of ids (must be in ascending order)
    function _initializeIds(
        StrategyType _strategyType,
        uint256[] memory ids
    ) internal {
        // initialize the range or the list
        if (_strategyType == StrategyType.All) {
            if (ids.length != 0) {
                revert InvalidIds();
            }
        } else if (_strategyType == StrategyType.Range) {
            if (ids.length != 2) {
                revert InvalidIds();
            }

            uint256 start = ids[0];
            uint256 end = ids[1];

            if (end < start) {
                revert InvalidIds();
            }

            range = StrategyRange(start, end);
        } else if (_strategyType == StrategyType.List) {
            uint256 length = ids.length;
            if (length == 0) {
                revert InvalidIds();
            }

            uint256 previousId;
            uint256 id;
            for (uint256 i; i < length; i++) {
                id = ids[i];
                if (i > 0) {
                    if (id <= previousId) {
                        revert InvalidIds();
                    }
                }
                acceptedIds[id] = true;
                previousId = id;
            }
        }
    }

    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
    /*                      Callbacks                      */
    /* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */

    /// @notice Handles the receipt of items from the collection
    function onERC1155Received(
        address,
        address,
        uint256 tokenId,
        uint256 amount,
        bytes calldata
    ) external returns (bytes4) {
        if (msg.sender != address(collection)) {
            revert InvalidCollection();
        }

        if (amount != 1) {
            revert InvalidAmount();
        }

        if (strategyType == StrategyType.Range) {
            if (tokenId < range.start || tokenId > range.end) {
                revert InvalidTokenId();
            }
        } else if (strategyType == StrategyType.List) {
            if (!acceptedIds[tokenId]) {
                revert InvalidTokenId();
            }
        }

        return this.onERC1155Received.selector;
    }
}
"
    },
    "src/Interfaces.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.7.5;

import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol";
import {IUniswapV4Router04} from "v4-router/interfaces/IUniswapV4Router04.sol";

/// @title Interfaces - Core interfaces for the strategy ecosystem
/// @author TokenWorks (https://token.works/)
/// @notice This file contains all the interfaces used by the strategy contracts

/// @notice Interface for Universal Router (legacy, kept for compatibility)
interface IUniversalRouter {
    /// @notice Thrown when a required command has failed
    error ExecutionFailed(uint256 commandIndex, bytes message);

    /// @notice Thrown when attempting to send ETH directly to the contract
    error ETHNotAccepted();

    /// @notice Thrown when executing commands with an expired deadline
    error TransactionDeadlinePassed();

    /// @notice Thrown when attempting to execute commands and an incorrect number of inputs are provided
    error LengthMismatch();

    // @notice Thrown when an address that isn't WETH tries to send ETH to the router without calldata
    error InvalidEthSender();

    /// @notice Executes encoded commands along with provided inputs. Reverts if deadline has expired.
    /// @param commands A set of concatenated commands, each 1 byte in length
    /// @param inputs An array of byte strings containing abi encoded inputs for each command
    /// @param deadline The deadline by which the transaction must be executed
    function execute(bytes calldata commands, bytes[] calldata inputs, uint256 deadline) external payable;
}

/// @notice Parameters for exact input single swaps (legacy, kept for compatibility)
struct ExactInputSingleParams {
    PoolKey poolKey;
    bool zeroForOne;
    uint128 amountIn;
    uint128 amountOutMinimum;
    bytes hookData;
}

/// @notice Interface for PunkStrategy contracts
/// @dev Interface for the original PunkStrategy token contract
interface IPunkStrategy {
    // View functions
    function loadingLiquidity() external view returns (bool);
    function owner() external view returns (address);
    function name() external pure returns (string memory);
    function symbol() external pure returns (string memory);
    function hookAddress() external view returns (address);
    function currentFees() external view returns (uint256);
    function reward() external view returns (uint256);
    function lastPunkSalePrice() external view returns (uint256);
    function priceMultiplier() external view returns (uint256);
    function canProcessPunkSale() external view returns (bool);

    // Admin functions
    function loadLiquidity(address _hook) external payable;
    function transferEther(address _to, uint256 _amount) external payable;
    function setReward(uint256 _newReward) external;
    function setPriceMultiplier(uint256 _newMultiplier) external;
    function transferOwnership(address newOwner) external;

    // Mechanism functions
    function addFees() external payable;
    function buyPunkAndRelist(uint256 punkId) external returns (uint256);
    function processPunkSale() external returns (uint256);

    // Constants
    function MAX_SUPPLY() external pure returns (uint256);
    function DEADADDRESS() external pure returns (address);
}

/// @notice Interface for PunkStrategyHook contracts
interface IPunkStrategyHook {
    // View functions
    function feeBips() external view returns (uint128);
    function prePunkSellBips() external view returns (uint128);
    function feeSplit() external view returns (IFeeSplit);
    function calculateFee(bool isBuying) external view returns (uint128);
    function getHookPermissions() external pure returns (Hooks.Permissions memory);

    // Admin functions
    function transferToken(address _token, address _to, uint256 _amount) external payable;
    function updateFeeBips(uint128 _feeBips) external;
    function updateManualFees(bool _manuallyProcessFees) external;
    function updateFeeSplit(IFeeSplit _feeSplit) external;

    // Mechanism functions
    function feeCooldown() external;
    function punksAreAccumulating() external;
    function processAccumulatedFees() external;
}

/// @notice Interface for fee splitting contracts
interface IFeeSplit {
    function processDeposit() external payable;
}

/// @notice Offer struct for CryptoPunks marketplace
struct Offer {
    bool isForSale;
    uint256 punkIndex;
    address seller;
    uint256 minValue;
    address onlySellTo;
}

/// @notice Interface for CryptoPunks contract
/// @dev Interface for interacting with the original CryptoPunks contract
interface IPunks {
    function buyPunk(uint256 punkIndex) external payable;
    function offerPunkForSale(uint256 punkIndex, uint256 minSalePriceInWei) external;
    function punksOfferedForSale(uint256 punkId)
        external
        view
        returns (bool isForSale, uint256 punkIndex, address seller, uint256 minValue, address onlySellTo);
    function balanceOf(address owner) external view returns (uint256);
    function punkIndexToAddress(uint256 punkIndex) external view returns (address);
    function withdraw() external;
    function pendingWithdrawals(address owner) external view returns (uint256);
    function transferPunk(address to, uint256 punkIndex) external;
}

/// @notice Interface for ERC20 tokens (standard interface)
interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

/// @notice Interface for ERC721 NFT collections
/// @dev Standard ERC721 interface with additional owner() function for collection ownership
interface IERC721 {
    function balanceOf(address owner) external view returns (uint256 balance);
    function ownerOf(uint256 tokenId) external view returns (address owner);
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
    function safeTransferFrom(address from, address to, uint256 tokenId) external;
    function transferFrom(address from, address to, uint256 tokenId) external;
    function approve(address to, uint256 tokenId) external;
    function setApprovalForAll(address operator, bool approved) external;
    function getApproved(uint256 tokenId) external view returns (address operator);
    function isApprovedForAll(address owner, address operator) external view returns (bool);
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
    function owner() external view returns (address);
}

interface IStrategy {
    function factory() external view returns (address);
    function router() external view returns (address);
    function poolManager() external view returns (address);
    function owner() external view returns (address);
    function addFees() external payable;
    function setPriceMultiplier(uint256 _newMultiplier) external;
    function updateName(string memory _tokenName) external;
    function updateSymbol(string memory _tokenSymbol) external;
    function updateHookAddress(address _hookAddress) external;
    function increaseTransferAllowance(uint256 amountAllowed) external;
    function getTransferAllowance() external view returns (uint256);
    function getImplementation() external view returns (address);
    function upgradeToAndCall(address newImplementation, bytes memory data) external;
}

/// @notice Interface for NFTStrategy contracts
/// @dev Core interface for NFT-backed ERC20 strategy tokens
interface INFTStrategy is IStrategy {
    function initialize(
        address _collection,
        address _hook,
        string memory _tokenName,
        string memory _tokenSymbol,
        uint256 _buyIncrement,
        address _owner
    ) external;
    function nftForSale(uint256 tokenId) external view returns (uint256);
    function sellTargetNFT(uint256 tokenId) external payable;
}

/// @notice Interface for BaseStrategyFactory contracts
/// @dev Factory interface for deploying and managing NFTStrategy contracts
interface IBaseStrategyFactory {
    function loadingLiquidity() external view returns (bool);
    function owner() external view returns (address);
    function strategyImplementation() external view returns (address);
    function setStrategyImplementation(address _strategyImplementation) external returns (address);
    function updateHookAddress(address _hookAddress) external returns (address);
    function disableLaunchUpgradeable() external;
}

/// @notice Interface for NFTStrategyFactory contracts
/// @dev Factory interface for deploying and managing NFTStrategy contracts
interface INFTStrategyFactory is IBaseStrategyFactory {
    function nftStrategyImplementation() external view returns (address);
    function collectionToNFTStrategy(address collection) external view returns (address);
    function nftStrategyToCollection(address collection) external view returns (address);
    function setNftStrategyImplementation(address _nftStrategyImplementation) external returns (address);
    function ownerLaunchNFTStrategy(
        address collection,
        string memory tokenName,
        string memory tokenSymbol,
        address collectionOwner,
        uint256 buyIncrement
    ) external payable returns (address);
}

/// @notice Interface for NFTStrategyHook contracts
/// @dev Hook interface for fee management and distribution
interface INFTStrategyHook {
    function adminUpdateFeeAddress(address collection, address destination) external;
}

/// @notice Interface for NFTStrategyRange contracts
interface INFTStrategyRange {
    function addFees() external payable;
    function setPriceMultiplier(uint256 _newMultiplier) external;
    function updateName(string memory _tokenName) external;
    function updateSymbol(string memory _tokenSymbol) external;
    function nftForSale(uint256 tokenId) external view returns (uint256);
    function midSwap() external view returns (bool);
    function setMidSwap(bool value) external;
    function sellTargetNFT(uint256 tokenId) external payable;
    function increaseTransferAllowance(uint256 amountAllowed) external;
    function getTransferAllowance() external view returns (uint256);
}

/// @notice Interface for NFTStrategyRangeFactory contracts
interface INFTStrategyRangeFactory {
    function poolManager() external view returns (address);
    function loadingLiquidity() external view returns (bool);
    function deployerBuying() external view returns (bool);
    function owner() external view returns (address);
    function setRouter(address _router, bool status) external;
    function collectionToNFTStrategy(address collection) external view returns (address);
    function nftStrategyToCollection(address collection) external view returns (address);
    function routerRestrict() external view returns (bool);
    function setRouterRestrict(bool status) external;
    function validTransfer(address to, address from, address tokenAddress) external view returns (bool);
}

/// @notice Interface for NFTStrategyRangeHook contracts
interface INFTStrategyRangeHook {
    function adminUpdateFeeAddress(address collection, address destination) external;
}

/// @notice Interface for router validation
interface IValidRouter {
    function msgSender() external view returns (address);
}

/// @notice Interface for PunkStrategyPatch contracts
/// @dev Interface for patch contracts that manage PunkStrategy operations
interface IPunkStrategyPatch {
    function updateFeeBips(uint128 _feeBips) external;
    function setPriceMultiplier(uint256 _newMultiplier) external;
    function transferOwnership(address newOwner) external;
    function transferEther(address _to, uint256 _amount) external payable;
    function setReward(uint256 _newReward) external;
    function setTwapIncrement(uint256 _newIncrement) external;
    function setTwapDelayInBlocks(uint256 _newDelay) external;
    function buyPunkAndRelist(uint256 punkId) external returns (uint256);
    function processPunkSale() external returns (uint256);
    function processTokenTwap() external;
    function transferPunkStrategyOwnership(address newOwner) external;
    function addFees() external payable;
    function transferToken(address _token, address _to, uint256 _amount) external payable;
    function updateManualFees(bool _manuallyProcessFees) external;
    function updateFeeSplit(IFeeSplit _feeSplit) external;
    function owner() external view returns (address);
}

interface IGlobalDistributor {
    function isGlobalDistributor(address) external view returns (bool);
}

interface IERC20Strategy is IStrategy {
    function initialize(
        address _collection,
        uint256 _bagSize,
        address _hook,
        string memory _tokenName,
        string memory _tokenSymbol,
        uint256 _buyIncrement,
        address _owner
    ) external;
}

interface IERC20StrategyFactory {
    function ownerLaunchStrategy(
        address token,
        uint256 bagSize,
        string memory tokenName,
        string memory tokenSymbol,
        address strategyFeeAddress,
        uint256 buyIncrement
    ) external payable returns (address);
}
"
    },
    "lib/solady/src/utils/LibClone.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Minimal proxy library.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibClone.sol)
/// @author Minimal proxy by 0age (https://github.com/0age)
/// @author Clones with immutable args by wighawag, zefram.eth, Saw-mon & Natalie
/// (https://github.com/Saw-mon-and-Natalie/clones-with-immutable-args)
/// @author Minimal ERC1967 proxy by jtriley-eth (https://github.com/jtriley-eth/minimum-viable-proxy)
///
/// @dev Minimal proxy:
/// Although the sw0nt pattern saves 5 gas over the ERC1167 pattern during runtime,
/// it is not supported out-of-the-box on Etherscan. Hence, we choose to use the 0age pattern,
/// which saves 4 gas over the ERC1167 pattern during runtime, and has the smallest bytecode.
/// - Automatically verified on Etherscan.
///
/// @dev Minimal proxy (PUSH0 variant):
/// This is a new minimal proxy that uses the PUSH0 opcode introduced during Shanghai.
/// It is optimized first for minimal runtime gas, then for minimal bytecode.
/// The PUSH0 clone functions are intentionally postfixed with a jarring "_PUSH0" as
/// many EVM chains may not support the PUSH0 opcode in the early months after Shanghai.
/// Please use with caution.
/// - Automatically verified on Etherscan.
///
/// @dev Clones with immutable args (CWIA):
/// The implementation of CWIA here does NOT append the immutable args into the calldata
/// passed into delegatecall. It is simply an ERC1167 minimal proxy with the immutable arguments
/// appended to the back of the runtime bytecode.
/// - Uses the identity precompile (0x4) to copy args during deployment.
///
/// @dev Minimal ERC1967 proxy:
/// A minimal ERC1967 proxy, intended to be upgraded with UUPS.
/// This is NOT the same as ERC1967Factory's transparent proxy, which includes admin logic.
/// - Automatically verified on Etherscan.
///
/// @dev Minimal ERC1967 proxy with immutable args:
/// - Uses the identity precompile (0x4) to copy args during deployment.
/// - Automatically verified on Etherscan.
///
/// @dev ERC1967I proxy:
/// A variant of the minimal ERC1967 proxy, with a special code path that activates
/// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the
/// `implementation` address. The returned implementation is guaranteed to be valid if the
/// keccak256 of the proxy's code is equal to `ERC1967I_CODE_HASH`.
///
/// @dev ERC1967I proxy with immutable args:
/// A variant of the minimal ERC1967 proxy, with a special code path that activates
/// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the
/// - Uses the identity precompile (0x4) to copy args during deployment.
///
/// @dev Minimal ERC1967 beacon proxy:
/// A minimal beacon proxy, intended to be upgraded with an upgradable beacon.
/// - Automatically verified on Etherscan.
///
/// @dev Minimal ERC1967 beacon proxy with immutable args:
/// - Uses the identity precompile (0x4) to copy args during deployment.
/// - Automatically verified on Etherscan.
///
/// @dev ERC1967I beacon proxy:
/// A variant of the minimal ERC1967 beacon proxy, with a special code path that activates
/// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the
/// `implementation` address. The returned implementation is guaranteed to be valid if the
/// keccak256 of the proxy's code is equal to `ERC1967I_CODE_HASH`.
///
/// @dev ERC1967I proxy with immutable args:
/// A variant of the minimal ERC1967 beacon proxy, with a special code path that activates
/// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the
/// - Uses the identity precompile (0x4) to copy args during deployment.
library LibClone {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The keccak256 of deployed code for the clone proxy,
    /// with the implementation set to `address(0)`.
    bytes32 internal constant CLONE_CODE_HASH =
        0x48db2cfdb2853fce0b464f1f93a1996469459df3ab6c812106074c4106a1eb1f;

    /// @dev The keccak256 of deployed code for the PUSH0 proxy,
    /// with the implementation set to `address(0)`.
    bytes32 internal constant PUSH0_CLONE_CODE_HASH =
        0x67bc6bde1b84d66e267c718ba44cf3928a615d29885537955cb43d44b3e789dc;

    /// @dev The keccak256 of deployed code for the ERC-1167 CWIA proxy,
    /// with the implementation set to `address(0)`.
    bytes32 internal constant CWIA_CODE_HASH =
        0x3cf92464268225a4513da40a34d967354684c32cd0edd67b5f668dfe3550e940;

    /// @dev The keccak256 of the deployed code for the ERC1967 proxy.
    bytes32 internal constant ERC1967_CODE_HASH =
        0xaaa52c8cc8a0e3fd27ce756cc6b4e70c51423e9b597b11f32d3e49f8b1fc890d;

    /// @dev The keccak256 of the deployed code for the ERC1967I proxy.
    bytes32 internal constant ERC1967I_CODE_HASH =
        0xce700223c0d4cea4583409accfc45adac4a093b3519998a9cbbe1504dadba6f7;

    /// @dev The keccak256 of the deployed code for the ERC1967 beacon proxy.
    bytes32 internal constant ERC1967_BEACON_PROXY_CODE_HASH =
        0x14044459af17bc4f0f5aa2f658cb692add77d1302c29fe2aebab005eea9d1162;

    /// @dev The keccak256 of the deployed code for the ERC1967 beacon proxy.
    bytes32 internal constant ERC1967I_BEACON_PROXY_CODE_HASH =
        0xf8c46d2793d5aa984eb827aeaba4b63aedcab80119212fce827309788735519a;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Unable to deploy the clone.
    error DeploymentFailed();

    /// @dev The salt must start with either the zero address or `by`.
    error SaltDoesNotStartWith();

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  MINIMAL PROXY OPERATIONS                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Deploys a clone of `implementation`.
    function clone(address implementation) internal returns (address instance) {
        instance = clone(0, implementation);
    }

    /// @dev Deploys a clone of `implementation`.
    /// Deposits `value` ETH during deployment.
    function clone(uint256 value, address implementation) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            /**
             * --------------------------------------------------------------------------+
             * CREATION (9 bytes)                                                        |
             * --------------------------------------------------------------------------|
             * Opcode     | Mnemonic          | Stack     | Memory                       |
             * --------------------------------------------------------------------------|
             * 60 runSize | PUSH1 runSize     | r         |                              |
             * 3d         | RETURNDATASIZE    | 0 r       |                              |
             * 81         | DUP2              | r 0 r     |                              |
             * 60 offset  | PUSH1 offset      | o r 0 r   |                              |
             * 3d         | RETURNDATASIZE    | 0 o r 0 r |                              |
             * 39         | CODECOPY          | 0 r       | [0..runSize): runtime code   |
             * f3         | RETURN            |           | [0..runSize): runtime code   |
             * --------------------------------------------------------------------------|
             * RUNTIME (44 bytes)                                                        |
             * --------------------------------------------------------------------------|
             * Opcode  | Mnemonic       | Stack                  | Memory                |
             * --------------------------------------------------------------------------|
             *                                                                           |
             * ::: keep some values in stack ::::::::::::::::::::::::::::::::::::::::::: |
             * 3d      | RETURNDATASIZE | 0                      |                       |
             * 3d      | RETURNDATASIZE | 0 0                    |                       |
             * 3d      | RETURNDATASIZE | 0 0 0                  |                       |
             * 3d      | RETURNDATASIZE | 0 0 0 0                |                       |
             *                                                                           |
             * ::: copy calldata to memory ::::::::::::::::::::::::::::::::::::::::::::: |
             * 36      | CALLDATASIZE   | cds 0 0 0 0            |                       |
             * 3d      | RETURNDATASIZE | 0 cds 0 0 0 0          |                       |
             * 3d      | RETURNDATASIZE | 0 0 cds 0 0 0 0        |                       |
             * 37      | CALLDATACOPY   | 0 0 0 0                | [0..cds): calldata    |
             *                                                                           |
             * ::: delegate call to the implementation contract :::::::::::::::::::::::: |
             * 36      | CALLDATASIZE   | cds 0 0 0 0            | [0..cds): calldata    |
             * 3d      | RETURNDATASIZE | 0 cds 0 0 0 0          | [0..cds): calldata    |
             * 73 addr | PUSH20 addr    | addr 0 cds 0 0 0 0     | [0..cds): calldata    |
             * 5a      | GAS            | gas addr 0 cds 0 0 0 0 | [0..cds): calldata    |
             * f4      | DELEGATECALL   | success 0 0            | [0..cds): calldata    |
             *                                                                           |
             * ::: copy return data to memory :::::::::::::::::::::::::::::::::::::::::: |
             * 3d      | RETURNDATASIZE | rds success 0 0        | [0..cds): calldata    |
             * 3d      | RETURNDATASIZE | rds rds success 0 0    | [0..cds): calldata    |
             * 93      | SWAP4          | 0 rds success 0 rds    | [0..cds): calldata    |
             * 80      | DUP1           | 0 0 rds success 0 rds  | [0..cds): calldata    |
             * 3e      | RETURNDATACOPY | success 0 rds          | [0..rds): returndata  |
             *                                                                           |
             * 60 0x2a | PUSH1 0x2a     | 0x2a success 0 rds     | [0..rds): returndata  |
             * 57      | JUMPI          | 0 rds                  | [0..rds): returndata  |
             *                                                                           |
             * ::: revert :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * fd      | REVERT         |                        | [0..rds): returndata  |
             *                                                                           |
             * ::: return :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 5b      | JUMPDEST       | 0 rds                  | [0..rds): returndata  |
             * f3      | RETURN         |                        | [0..rds): returndata  |
             * --------------------------------------------------------------------------+
             */
            mstore(0x21, 0x5af43d3d93803e602a57fd5bf3)
            mstore(0x14, implementation)
            mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73)
            instance := create(value, 0x0c, 0x35)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Deploys a deterministic clone of `implementation` with `salt`.
    function cloneDeterministic(address implementation, bytes32 salt)
        internal
        returns (address instance)
    {
        instance = cloneDeterministic(0, implementation, salt);
    }

    /// @dev Deploys a deterministic clone of `implementation` with `salt`.
    /// Deposits `value` ETH during deployment.
    function cloneDeterministic(uint256 value, address implementation, bytes32 salt)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x21, 0x5af43d3d93803e602a57fd5bf3)
            mstore(0x14, implementation)
            mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73)
            instance := create2(value, 0x0c, 0x35, salt)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Returns the initialization code of the clone of `implementation`.
    function initCode(address implementation) internal pure returns (bytes memory c) {
        /// @solidity memory-safe-assembly
        assembly {
            c := mload(0x40)
            mstore(add(c, 0x40), 0x5af43d3d93803e602a57fd5bf30000000000000000000000)
            mstore(add(c, 0x28), implementation)
            mstore(add(c, 0x14), 0x602c3d8160093d39f33d3d3d3d363d3d37363d73)
            mstore(c, 0x35) // Store the length.
            mstore(0x40, add(c, 0x60)) // Allocate memory.
        }
    }

    /// @dev Returns the initialization code hash of the clone of `implementation`.
    function initCodeHash(address implementation) internal pure returns (bytes32 hash) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x21, 0x5af43d3d93803e602a57fd5bf3)
            mstore(0x14, implementation)
            mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73)
            hash := keccak256(0x0c, 0x35)
            mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Returns the address of the clone of `implementation`, with `salt` by `deployer`.
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function predictDeterministicAddress(address implementation, bytes32 salt, address deployer)
        internal
        pure
        returns (address predicted)
    {
        bytes32 hash = initCodeHash(implementation);
        predicted = predictDeterministicAddress(hash, salt, deployer);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*          MINIMAL PROXY OPERATIONS (PUSH0 VARIANT)          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Deploys a PUSH0 clone of `implementation`.
    function clone_PUSH0(address implementation) internal returns (address instance) {
        instance = clone_PUSH0(0, implementation);
    }

    /// @dev Deploys a PUSH0 clone of `implementation`.
    /// Deposits `value` ETH during deployment.
    funct

Tags:
ERC20, ERC721, ERC1155, ERC165, Multisig, Mintable, Burnable, Non-Fungible, Swap, Liquidity, Upgradeable, Multi-Signature, Factory|addr:0x698ba054a8dd4535753aa14e6d67ddaa59f5328d|verified:true|block:23743614|tx:0x366c7c6a6a2169759ae274119a1e944e1f5000fb2e25f9746938690a8a5d7e23|first_check:1762511207

Submitted on: 2025-11-07 11:26:48

Comments

Log in to comment.

No comments yet.