// File: src/Create/contracts/v1/RandomDeployer.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import {RandomMintModule} from "create/contracts/v1/RandomMintModule.sol";
import {RandomEditionRenderer} from "create/contracts/v1/RandomEditionRenderer.sol";
import {Ownable} from "solady/auth/Ownable.sol";

/// @dev This contract is used to deploy RandomMintModule and RandomEditionRenderer
/// to a deterministic addresses while using constructor arguments.
contract RandomDeployer is Ownable {
    constructor() {
        _initializeOwner(tx.origin);
    }

    function deploy(address mintPayoutAddress)
        external
        onlyOwner
        returns (address randomMintModuleAddress, address randomEditionRendererAddress)
    {
        randomMintModuleAddress = address(new RandomMintModule(mintPayoutAddress));
        randomEditionRendererAddress = address(new RandomEditionRenderer(randomMintModuleAddress));
    }
}

// File: src/Create/contracts/v1/RandomMintModule.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import {IRandomMintModule, IConfigurable} from "create/interfaces/v1/IRandomMintModule.sol";
import {IMintPayout} from "create/interfaces/v1/IMintPayout.sol";
import {IMintContract} from "create/interfaces/v1/IMintContract.sol";
import {IOwnable} from "create/interfaces/v1/IOwnable.sol";
import {BasicMintConfiguration} from "create/interfaces/v1/BasicMintConfiguration.sol";
import {Version} from "create/contracts/v1/Version.sol";

contract RandomMintModule is IRandomMintModule, Version {
    IMintPayout public immutable mintPayout;

    mapping(address => BasicMintConfiguration) private _configurations;
    mapping(address => mapping(address => uint256)) private _mintedByAddress;
    mapping(address => uint256) public mintedByContract;
    mapping(address => mapping(uint256 => bytes32)) public tokenRandom;

    /// @notice Emitted when quantity is zero.
    error InvalidQuantity();
    /// @notice Emitted if the collector is minting too many tokens per transaction.
    error TooManyTokensPerTransaction();
    /// @notice Emitted if the collector is minting too many tokens per wallet.
    error TooManyTokensPerCollector();
    /// @notice Emitted if the collector is minting more tokens than this module is allowed to mint.
    error TooManyTokensForModule();
    /// @notice Emitted if the mint has not started yet.
    error MintNotStarted();
    /// @notice Emitted if the mint has ended.
    error MintEnded();
    /// @notice Emitted when the value sent is incorrect.
    error IncorrectPayment();
    /// @notice Emitted when the max supply is reached.
    error MaxSupplyReached();
    /// @notice Emitted when a function is called by a non-owner.
    error OnlyOwner();
    /// @notice Emitted when a token has already been randomized.
    error AlreadyRandomized();
    /// @notice Emitted when an invalid token ID is provided.
    error InvalidTokenId();

    constructor(address _mintPayout) Version(1) {
        mintPayout = IMintPayout(_mintPayout);
    }

    /// @inheritdoc IConfigurable
    function updateConfiguration(bytes calldata args) external override {
        BasicMintConfiguration memory _config = abi.decode(args, (BasicMintConfiguration));

        _configurations[msg.sender] = _config;
        emit ConfigurationUpdated(msg.sender, _config);
    }

    /// @inheritdoc IRandomMintModule
    function configuration(address _contract) external view returns (BasicMintConfiguration memory) {
        return _configurations[_contract];
    }

    /// @inheritdoc IRandomMintModule
    function mint(address _contract, address _to, address _referrer, uint256 _quantity) external payable {
        _mint(_contract, _to, _referrer, _quantity);
    }

    /// @inheritdoc IRandomMintModule
    function mint_efficient_7e80c46e(address _contract, address _to, address _referrer, uint256 _quantity)
        external
        payable
    {
        _mint(_contract, _to, _referrer, _quantity);
    }

    /// @notice The implementation of the mint function.
    /// @dev This is implemented as an internal function to share the logic between the `mint` and `mint_efficient_7e80c46e` functions.
    /// See the documentation for those functions for information on the parameters.
    function _mint(address _contract, address _to, address _referrer, uint256 _quantity) internal {
        BasicMintConfiguration memory config = _configurations[_contract];

        if (_quantity == 0) revert InvalidQuantity();
        if (config.maxPerTransaction > 0 && _quantity > config.maxPerTransaction) revert TooManyTokensPerTransaction();

        if (config.maxPerWallet > 0) {
            if (_mintedByAddress[_contract][_to] + _quantity > config.maxPerWallet) {
                revert TooManyTokensPerCollector();
            }
        }

        if (config.maxForModule > 0 && mintedByContract[_contract] + _quantity > config.maxForModule) {
            revert TooManyTokensForModule();
        }

        if (block.timestamp < config.mintStart) revert MintNotStarted();
        if (config.mintEnd > 0 && block.timestamp > config.mintEnd) revert MintEnded();

        uint256 protocolFee = mintPayout.protocolFee();
        if (msg.value != (config.price + protocolFee) * _quantity) revert IncorrectPayment();

        uint256 totalMinted = IMintContract(_contract).totalMinted();
        if (config.maxSupply > 0 && totalMinted + _quantity > config.maxSupply) {
            revert MaxSupplyReached();
        }

        _mintedByAddress[_contract][_to] += _quantity;
        mintedByContract[_contract] += _quantity;

        mintPayout.mintDeposit{value: msg.value}(_contract, msg.sender, _referrer, _quantity);
        IMintContract(_contract).mint(_to, _quantity);

        for (uint256 i = 1; i <= _quantity;) {
            _setRandomForToken(_contract, totalMinted + i);
            unchecked {
                ++i;
            }
        }
    }

    function _setRandomForToken(address _contract, uint256 _tokenId) internal {
        tokenRandom[_contract][_tokenId] =
            keccak256(abi.encodePacked(_contract, _tokenId, block.prevrandao, blockhash(block.number - 1)));
    }

    /// @inheritdoc IRandomMintModule
    function randomizeTokens(address _contract, uint256[] calldata _tokenIds) external {
        address owner = IOwnable(_contract).owner();
        if (msg.sender != owner) revert OnlyOwner();

        uint256 totalMinted = IMintContract(_contract).totalMinted();

        for (uint256 i; i < _tokenIds.length;) {
            uint256 _tokenId = _tokenIds[i];
            if (tokenRandom[_contract][_tokenId] != bytes32(0)) {
                revert AlreadyRandomized();
            }

            if (_tokenId == 0 || _tokenId > totalMinted) {
                revert InvalidTokenId();
            }

            _setRandomForToken(_contract, _tokenId);

            unchecked {
                ++i;
            }
        }
    }
}

// File: src/Create/contracts/v1/RandomEditionRenderer.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import {Strings} from "openzeppelin/utils/Strings.sol";
import {Base64} from "openzeppelin/utils/Base64.sol";
import {IMetadataRenderer} from "create/interfaces/v1/IMetadataRenderer.sol";
import {IRandomEditionRenderer, IConfigurable} from "create/interfaces/v1/IRandomEditionRenderer.sol";
import {
    RandomEditionRendererConfiguration,
    RandomTokenConfiguration,
    TokenTrait
} from "create/interfaces/v1/RandomEditionRendererConfiguration.sol";
import {IRandomMintModule} from "create/interfaces/v1/IRandomMintModule.sol";
import {Version} from "create/contracts/v1/Version.sol";
import {RandomTokenSelection} from "create/contracts/v1/RandomTokenSelection.sol";

contract RandomEditionRenderer is IMetadataRenderer, IRandomEditionRenderer, Version {
    IRandomMintModule public immutable randomMintModule;
    mapping(address => RandomEditionRendererConfiguration) _configurations;

    error InvalidTokenConfiguration();

    using RandomTokenSelection for RandomEditionRendererConfiguration;

    constructor(address _randomMintModule) Version(1) {
        randomMintModule = IRandomMintModule(_randomMintModule);
    }

    /// @inheritdoc IConfigurable
    function updateConfiguration(bytes calldata args) external {
        RandomEditionRendererConfiguration memory config = abi.decode(args, (RandomEditionRendererConfiguration));
        if (config.totalWeight == 0) {
            revert InvalidTokenConfiguration();
        }

        if (config.tokens.length == 0) {
            revert InvalidTokenConfiguration();
        }

        _configurations[msg.sender].totalWeight = config.totalWeight;
        delete _configurations[msg.sender].tokens;
        uint32 totalWeight;
        for (uint256 i; i < config.tokens.length;) {
            RandomTokenConfiguration memory tokenConfig = config.tokens[i];
            RandomTokenConfiguration storage dest = _configurations[msg.sender].tokens.push();
            dest.weight = tokenConfig.weight;
            dest.tokenName = tokenConfig.tokenName;
            dest.tokenDescription = tokenConfig.tokenDescription;
            dest.imageIPFSHash = tokenConfig.imageIPFSHash;
            dest.animationIPFSHash = tokenConfig.animationIPFSHash;
            dest.animationMimeType = tokenConfig.animationMimeType;
            for (uint256 j; j < tokenConfig.traits.length;) {
                TokenTrait memory trait = tokenConfig.traits[j];
                dest.traits.push(trait);
                unchecked {
                    ++j;
                }
            }

            totalWeight += tokenConfig.weight;
            unchecked {
                ++i;
            }
        }

        if (totalWeight != config.totalWeight) {
            revert InvalidTokenConfiguration();
        }

        emit ConfigurationUpdated(msg.sender, config);
    }

    /// @inheritdoc IRandomEditionRenderer
    function configuration(address mintContract)
        external
        view
        override
        returns (RandomEditionRendererConfiguration memory)
    {
        return _configurations[mintContract];
    }

    function _tokenConfiguration(address mintContract, uint256 tokenId)
        internal
        view
        returns (RandomTokenConfiguration memory)
    {
        bytes32 random = randomMintModule.tokenRandom(mintContract, tokenId);
        return _configurations[mintContract].getConfiguredToken(random);
    }

    function tokenURIJSON(address mintContract, uint256 tokenId) public view returns (string memory) {
        RandomTokenConfiguration memory config = _tokenConfiguration(mintContract, tokenId);

        string memory name =
            string.concat('"name":"', escapeJsonString(config.tokenName), " #", Strings.toString(tokenId), '"');

        string memory description = bytes(config.tokenDescription).length == 0
            ? ""
            : string.concat(',"description":"', escapeJsonString(config.tokenDescription), '"');

        string memory image = string.concat(',"image":"ipfs://', config.imageIPFSHash, '"');

        string memory animation = bytes(config.animationIPFSHash).length == 0
            ? ""
            : string.concat(
                ',"animation_url":"ipfs://',
                config.animationIPFSHash,
                '","content":{"mime":"',
                config.animationMimeType,
                '","uri":"ipfs://',
                config.animationIPFSHash,
                '"}'
            );

        string memory attributes;
        if (config.traits.length > 0) {
            attributes = ',"attributes":[';
            for (uint256 i; i < config.traits.length;) {
                attributes = string.concat(
                    attributes,
                    '{"trait_type":"',
                    escapeJsonString(config.traits[i].traitType),
                    '","value":"',
                    escapeJsonString(config.traits[i].traitValue),
                    '"}'
                );

                if (i < config.traits.length - 1) {
                    attributes = string.concat(attributes, ",");
                }

                unchecked {
                    ++i;
                }
            }
            attributes = string.concat(attributes, "]");
        }

        return string.concat("{", name, description, image, animation, attributes, "}");
    }

    /// @inheritdoc IMetadataRenderer
    function tokenURI(uint256 tokenId) external view override returns (string memory) {
        return string.concat("data:application/json;base64,", Base64.encode(bytes(tokenURIJSON(msg.sender, tokenId))));
    }

    // JSON escaping code copied from https://github.com/bmeredith/solidity-json-writer
    // Used under MIT license, (c) 2016-2020 zOS Global Limited

    bytes1 constant BACKSLASH = bytes1(uint8(92));
    bytes1 constant BACKSPACE = bytes1(uint8(8));
    bytes1 constant CARRIAGE_RETURN = bytes1(uint8(13));
    bytes1 constant DOUBLE_QUOTE = bytes1(uint8(34));
    bytes1 constant FORM_FEED = bytes1(uint8(12));
    bytes1 constant FRONTSLASH = bytes1(uint8(47));
    bytes1 constant HORIZONTAL_TAB = bytes1(uint8(9));
    bytes1 constant NEWLINE = bytes1(uint8(10));

    function escapeJsonString(string memory value) private pure returns (string memory str) {
        bytes memory b = bytes(value);
        bool foundEscapeChars;

        for (uint256 i; i < b.length; i++) {
            if (b[i] == BACKSLASH) {
                foundEscapeChars = true;
                break;
            } else if (b[i] == DOUBLE_QUOTE) {
                foundEscapeChars = true;
                break;
            } else if (b[i] == FRONTSLASH) {
                foundEscapeChars = true;
                break;
            } else if (b[i] == HORIZONTAL_TAB) {
                foundEscapeChars = true;
                break;
            } else if (b[i] == FORM_FEED) {
                foundEscapeChars = true;
                break;
            } else if (b[i] == NEWLINE) {
                foundEscapeChars = true;
                break;
            } else if (b[i] == CARRIAGE_RETURN) {
                foundEscapeChars = true;
                break;
            } else if (b[i] == BACKSPACE) {
                foundEscapeChars = true;
                break;
            }
        }

        if (!foundEscapeChars) {
            return value;
        }

        for (uint256 i; i < b.length; i++) {
            if (b[i] == BACKSLASH) {
                str = string(abi.encodePacked(str, "\\\\"));
            } else if (b[i] == DOUBLE_QUOTE) {
                str = string(abi.encodePacked(str, '\\"'));
            } else if (b[i] == FRONTSLASH) {
                str = string(abi.encodePacked(str, "\\/"));
            } else if (b[i] == HORIZONTAL_TAB) {
                str = string(abi.encodePacked(str, "\\t"));
            } else if (b[i] == FORM_FEED) {
                str = string(abi.encodePacked(str, "\\f"));
            } else if (b[i] == NEWLINE) {
                str = string(abi.encodePacked(str, "\\n"));
            } else if (b[i] == CARRIAGE_RETURN) {
                str = string(abi.encodePacked(str, "\\r"));
            } else if (b[i] == BACKSPACE) {
                str = string(abi.encodePacked(str, "\\b"));
            } else {
                str = string(abi.encodePacked(str, b[i]));
            }
        }

        return str;
    }
}

// File: lib/solady/src/auth/Ownable.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The caller is not authorized to call the function.
    error Unauthorized();

    /// @dev The `newOwner` cannot be the zero address.
    error NewOwnerIsZeroAddress();

    /// @dev The `pendingOwner` does not have a valid handover request.
    error NoHandoverRequest();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
    /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
    /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
    /// despite it not being as lightweight as a single argument event.
    event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);

    /// @dev An ownership handover to `pendingOwner` has been requested.
    event OwnershipHandoverRequested(address indexed pendingOwner);

    /// @dev The ownership handover to `pendingOwner` has been canceled.
    event OwnershipHandoverCanceled(address indexed pendingOwner);

    /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
    uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
        0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;

    /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
        0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;

    /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
        0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The owner slot is given by: `not(_OWNER_SLOT_NOT)`.
    /// It is intentionally chosen to be a high value
    /// to avoid collision with lower slots.
    /// The choice of manual storage layout is to enable compatibility
    /// with both regular and upgradeable contracts.
    uint256 private constant _OWNER_SLOT_NOT = 0x8b78c6d8;

    /// The ownership handover slot of `newOwner` is given by:
    /// ```
    ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
    ///     let handoverSlot := keccak256(0x00, 0x20)
    /// ```
    /// It stores the expiry timestamp of the two-step ownership handover.
    uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     INTERNAL FUNCTIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Initializes the owner directly without authorization guard.
    /// This function must be called upon initialization,
    /// regardless of whether the contract is upgradeable or not.
    /// This is to enable generalization to both regular and upgradeable contracts,
    /// and to save gas in case the initial owner is not the caller.
    /// For performance reasons, this function will not check if there
    /// is an existing owner.
    function _initializeOwner(address newOwner) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Clean the upper 96 bits.
            newOwner := shr(96, shl(96, newOwner))
            // Store the new value.
            sstore(not(_OWNER_SLOT_NOT), newOwner)
            // Emit the {OwnershipTransferred} event.
            log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
        }
    }

    /// @dev Sets the owner directly without authorization guard.
    function _setOwner(address newOwner) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            let ownerSlot := not(_OWNER_SLOT_NOT)
            // Clean the upper 96 bits.
            newOwner := shr(96, shl(96, newOwner))
            // Emit the {OwnershipTransferred} event.
            log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
            // Store the new value.
            sstore(ownerSlot, newOwner)
        }
    }

    /// @dev Throws if the sender is not the owner.
    function _checkOwner() internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // If the caller is not the stored owner, revert.
            if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) {
                mstore(0x00, 0x82b42900) // `Unauthorized()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Returns how long a two-step ownership handover is valid for in seconds.
    /// Override to return a different value if needed.
    /// Made internal to conserve bytecode. Wrap it in a public function if needed.
    function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
        return 48 * 3600;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Allows the owner to transfer the ownership to `newOwner`.
    function transferOwnership(address newOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(shl(96, newOwner)) {
                mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
                revert(0x1c, 0x04)
            }
        }
        _setOwner(newOwner);
    }

    /// @dev Allows the owner to renounce their ownership.
    function renounceOwnership() public payable virtual onlyOwner {
        _setOwner(address(0));
    }

    /// @dev Request a two-step ownership handover to the caller.
    /// The request will automatically expire in 48 hours (172800 seconds) by default.
    function requestOwnershipHandover() public payable virtual {
        unchecked {
            uint256 expires = block.timestamp + _ownershipHandoverValidFor();
            /// @solidity memory-safe-assembly
            assembly {
                // Compute and set the handover slot to `expires`.
                mstore(0x0c, _HANDOVER_SLOT_SEED)
                mstore(0x00, caller())
                sstore(keccak256(0x0c, 0x20), expires)
                // Emit the {OwnershipHandoverRequested} event.
                log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
            }
        }
    }

    /// @dev Cancels the two-step ownership handover to the caller, if any.
    function cancelOwnershipHandover() public payable virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x20), 0)
            // Emit the {OwnershipHandoverCanceled} event.
            log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
        }
    }

    /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
    /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
    function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            let handoverSlot := keccak256(0x0c, 0x20)
            // If the handover does not exist, or has expired.
            if gt(timestamp(), sload(handoverSlot)) {
                mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
                revert(0x1c, 0x04)
            }
            // Set the handover slot to 0.
            sstore(handoverSlot, 0)
        }
        _setOwner(pendingOwner);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   PUBLIC READ FUNCTIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the owner of the contract.
    function owner() public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(not(_OWNER_SLOT_NOT))
        }
    }

    /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
    function ownershipHandoverExpiresAt(address pendingOwner)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the handover slot.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            // Load the handover slot.
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         MODIFIERS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Marks a function as only callable by the owner.
    modifier onlyOwner() virtual {
        _checkOwner();
        _;
    }
}

// File: src/Create/interfaces/v1/IRandomMintModule.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import {BasicMintConfiguration} from "create/interfaces/v1/BasicMintConfiguration.sol";
import {IConfigurable} from "create/interfaces/v1/IConfigurable.sol";

interface IRandomMintModuleEvents {
    /// @notice Emitted when a contract's mint configuration is updated.
    /// @param _contract The address of the contract being configured.
    /// @param _config The new configuration.
    event ConfigurationUpdated(address indexed _contract, BasicMintConfiguration _config);

    /// @notice Emitted when a token's random value is set.
    /// @param _contract The address of the contract.
    /// @param _tokenId The token ID.
    /// @param _random The random value.
    event TokenRandomSet(address indexed _contract, uint256 indexed _tokenId, bytes32 _random);
}

interface IRandomMintModule is IConfigurable, IRandomMintModuleEvents {
    /// @notice Retrieves the minting configuration for a contract.
    /// @param _contract The address of the contract.
    /// @return The current minting configuration.
    function configuration(address _contract) external view returns (BasicMintConfiguration memory);

    /// @notice Retrieves the token random value for a given token.
    /// @dev This will return the zero value if this hasn't been set yet.
    /// @param _contract The address of the contract.
    /// @param _tokenId The token ID.
    /// @return The token random value.
    function tokenRandom(address _contract, uint256 _tokenId) external view returns (bytes32);

    /// @notice Randomizes the provided tokens for a contract.
    /// @dev Tokens must not be randomized already.
    /// Caller must be the owner of the contract.
    /// @param _contract The address of the contract.
    /// @param _tokenIds The token IDs to randomize.
    function randomizeTokens(address _contract, uint256[] calldata _tokenIds) external;

    /// @notice Mints tokens for a NFT contract to a recipient.
    /// @dev Reverts if the mint does not work in the current configuration.
    /// @param _contract The address of the contract to mint for.
    /// @param _to The recipient of the tokens.
    /// @param _referrer The referrer of this mint, or the zero address if none.
    /// @param _quantity The quantity of tokens to mint.
    function mint(address _contract, address _to, address _referrer, uint256 _quantity) external payable;

    /// @notice Mints tokens for a NFT contract to a recipient.
    /// @dev Reverts if the mint does not work in the current configuration.
    /// This function is preferred over `mint` because the four byte signature is 0x00000000 which is cheaper to call.
    /// The implementation is identical to `mint`.
    /// @param _contract The address of the contract to mint for.
    /// @param _to The recipient of the tokens.
    /// @param _referrer The referrer of this mint, or the zero address if none.
    /// @param _quantity The quantity of tokens to mint.
    function mint_efficient_7e80c46e(address _contract, address _to, address _referrer, uint256 _quantity)
        external
        payable;
}

// File: src/Create/interfaces/v1/IMintPayout.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

interface IMintPayoutEvents {
    /// @notice Emitted when a deposit has been made.
    /// @param from The depositor's address.
    /// @param to The address receiving the deposit.
    /// @param reason The reason code for the deposit.
    /// @param amount The deposit amount.
    event Deposit(address from, address to, bytes4 reason, uint256 amount);

    /// @notice Emitted when a withdrawal has been made.
    /// @param from The address withdrawing.
    /// @param to The address receiving the withdrawn funds.
    /// @param amount The withdrawal amount.
    event Withdraw(address from, address to, uint256 amount);

    /// @notice Emitted during a mint deposit to provide additional context.
    /// @param depositedBy The address of the mint initiator.
    /// @param mintContract The mint contract address this mint deposit refers to.
    /// @param minter The address of the person minting.
    /// @param referrer The address of the referrer, or the zero address for no referrer.
    /// @param creator The address of the contract creator, or the protocol fee recipient if none.
    /// @param creatorPayout The amount being paid to the creator.
    /// @param referralPayout The amount being paid to the referrer.
    /// @param protocolPayout The amount being paid to the protocol.
    /// @param totalAmount The total deposit amount.
    /// @param quantity The number of tokens being minted.
    /// @param protocolFee The per-mint fee for the protocol.
    event MintDeposit(
        address depositedBy,
        address mintContract,
        address minter,
        address referrer,
        address creator,
        uint256 creatorPayout,
        uint256 referralPayout,
        uint256 protocolPayout,
        uint256 totalAmount,
        uint256 quantity,
        uint256 protocolFee
    );

    /// @notice Emitted when the protocol fee is updated.
    /// @param fee The new protocol fee.
    event ProtocolFeeUpdated(uint256 fee);
}

interface IMintPayout is IMintPayoutEvents {
    function balanceOf(address owner) external view returns (uint256);
    function totalSupply() external view returns (uint256);

    /// @notice The current protocol fee per-mint.
    function protocolFee() external view returns (uint256 fee);

    /// @notice Sets the protocol fee per-mint.
    /// @dev Only callable by the owner.
    /// @param fee The new protocol fee.
    function setProtocolFee(uint256 fee) external;

    /// @notice Magic value used to represent the fees belonging to the protocol.
    function protocolFeeRecipientAccount() external view returns (address);

    /// @notice Withdraws from the protocol fee balance.
    /// @dev Only callable by the owner.
    /// @param to The address receiving the withdrawn funds.
    /// @param amount The withdrawal amount.
    function withdrawProtocolFee(address to, uint256 amount) external;

    /// @notice Deposits ether for a mint.
    /// @dev Ensure that `quantity` is > 0. The `protocolFee` should be per-mint, not the total taken.
    /// Will trigger a `MintDeposit` event, followed by `Deposit` events for:
    /// creator payout, protocol payout, and referrer payout (if a referrer is specified).
    /// @param mintContract The mint contract address this mint deposit refers to.
    /// @param minter The address of the minter.
    /// @param referrer The address of the referrer, or the zero address for no referrer.
    /// @param quantity The amount being minted.
    function mintDeposit(address mintContract, address minter, address referrer, uint256 quantity) external payable;

    /// @notice Deposits ether to an address.
    /// @param to The address receiving the deposit.
    /// @param reason The reason code for the deposit.
    function deposit(address to, bytes4 reason) external payable;

    /// @notice Deposits ether to multiple addresses.
    /// @dev The length of `recipients`, `amounts`, and `reasons` must be the same.
    /// @param recipients List of addresses receiving the deposits.
    /// @param amounts List of deposit amounts.
    /// @param reasons List of reason codes for the deposits.
    function depositBatch(address[] calldata recipients, uint256[] calldata amounts, bytes4[] calldata reasons)
        external
        payable;

    /// @notice Withdraws ether from the `msg.sender`'s account to a specified address.
    /// @param to The address receiving the withdrawn funds.
    /// @param amount The withdrawal amount.
    function withdraw(address to, uint256 amount) external;

    /// @notice Withdraws all ether from the `msg.sender`'s account to a specified address.
    /// @param to The address receiving the withdrawn funds.
    function withdrawAll(address to) external;
}

// File: src/Create/interfaces/v1/IMintContract.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import {IMetadataRenderer} from "create/interfaces/v1/IMetadataRenderer.sol";

interface IMintContractEvents {
    /// @notice Emitted when the royalty is updated.
    event RoyaltyUpdated(uint256 bps);
    /// @notice Emitted when a new mint module is added.
    event ModuleAdded(address module);
    /// @notice Emitted when a mint module is removed.
    event ModuleRemoved(address module);
    /// @notice Emitted when the metadata renderer is updated.
    event MetadataRendererUpdated(address renderer);
}

interface IMintContract is IMintContractEvents {
    /// @notice Mints tokens using approved mint modules.
    /// @param to The address receiving the minted tokens.
    /// @param quantity The quantity of tokens to mint.
    function mint(address to, uint256 quantity) external;

    /// @notice Mints tokens, callable only by the contract owner.
    /// @param to The address receiving the minted tokens.
    /// @param quantity The quantity of tokens to mint.
    function adminMint(address to, uint256 quantity) external;

    /// @notice Retrieves the payout recipient address for this mint contract.
    /// @return recipient address of the payout recipient.
    function payoutRecipient() external view returns (address recipient);

    /// @notice Returns the total number of tokens minted.
    /// @return total number of tokens minted.
    function totalMinted() external view returns (uint256 total);

    /// @notice Adds a new mint module as an approved minter.
    /// @dev Can only be executed by the owner of the contract.
    /// Must be approved in the MintModuleRegistry.
    /// @param mintModule The contract address of the mint module.
    function addMintModule(address mintModule) external;

    /// @notice Removes a mint module as an approved minter.
    /// @dev Can only be executed by the owner of the contract.
    /// @param mintModule The contract address of the mint module.
    function removeMintModule(address mintModule) external;

    /// @notice Returns whether a mint module is approved.
    /// @param mintModule The contract address of the mint module.
    /// @return isApproved Whether the mint module is approved.
    function isMintModuleApproved(address mintModule) external view returns (bool isApproved);

    /// @notice Updates configuration located in an external contract.
    /// @dev Can only be executed by the owner of the contract.
    /// The cardinality of `configurables` and `configData` must be the same.
    /// @param configurables The contract addresses to configure.
    /// @param configData The configuration data for the contracts.
    function updateExternalConfiguration(address[] calldata configurables, bytes[] calldata configData) external;

    /// @notice Sets the metadata renderer.
    /// @dev This will not request a metadata refresh. If needed, call `refreshMetadata`.
    /// @param renderer The new metadata renderer.
    function setMetadataRenderer(IMetadataRenderer renderer) external;

    /// @notice Returns the metadata renderer for this contract.
    /// @return metadataRenderer The metadata renderer.
    function metadataRenderer() external returns (IMetadataRenderer metadataRenderer);

    /// @notice Triggers a batch metadata update.
    function refreshMetadata() external;

    /// @notice Updates the royalty for this contract.
    /// @dev Can only be called by the contract owner.
    /// Emits a `RoyaltyUpdated` event.
    /// @param bps The new royalty.
    function setRoyalty(uint256 bps) external;

    /// @notice Returns the royalty for this contract.
    /// @return bps The royalty.
    function royaltyBps() external returns (uint256 bps);
}

// File: src/Create/interfaces/v1/IOwnable.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

interface IOwnable {
    /// @notice Returns the address of the current owner, or the zero address if there is none.
    /// @return The address of the owner of this contract.
    function owner() external view returns (address);
}

// File: src/Create/interfaces/v1/BasicMintConfiguration.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

struct BasicMintConfiguration {
    /// @notice Purchase cost per token.
    uint256 price;
    /// @notice UNIX timestamp of mint start.
    uint64 mintStart;
    /// @notice UNIX timestamp of mint end, or zero if open-ended.
    uint64 mintEnd;
    /// @notice Maximum token purchase limit per wallet, or zero if no limit.
    uint32 maxPerWallet;
    /// @notice Maximum tokens mintable per transaction, or zero if no limit.
    uint32 maxPerTransaction;
    /// @notice Maximum tokens mintable by this module, or zero if no limit.
    uint32 maxForModule;
    /// @notice Maximum tokens that can be minted in total, or zero if no max.
    uint32 maxSupply;
}

// File: src/Create/contracts/v1/Version.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

abstract contract Version {
    /// @notice The version of the contract.
    uint32 public immutable contractVersion;

    constructor(uint32 _contractVersion) {
        contractVersion = _contractVersion;
    }
}

// File: lib/openzeppelin-contracts/contracts/utils/Strings.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

// File: lib/openzeppelin-contracts/contracts/utils/Base64.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Base64.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides a set of functions to operate with Base64 strings.
 *
 * _Available since v4.5._
 */
library Base64 {
    /**
     * @dev Base64 Encoding/Decoding Table
     */
    string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    /**
     * @dev Converts a `bytes` to its Bytes64 `string` representation.
     */
    function encode(bytes memory data) internal pure returns (string memory) {
        /**
         * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
         * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
         */
        if (data.length == 0) return "";

        // Loads the table into memory
        string memory table = _TABLE;

        // Encoding takes 3 bytes chunks of binary data from `bytes` data parameter
        // and split into 4 numbers of 6 bits.
        // The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up
        // - `data.length + 2`  -> Round up
        // - `/ 3`              -> Number of 3-bytes chunks
        // - `4 *`              -> 4 characters for each chunk
        string memory result = new string(4 * ((data.length + 2) / 3));

        /// @solidity memory-safe-assembly
        assembly {
            // Prepare the lookup table (skip the first "length" byte)
            let tablePtr := add(table, 1)

            // Prepare result pointer, jump over length
            let resultPtr := add(result, 32)

            // Run over the input, 3 bytes at a time
            for {
                let dataPtr := data
                let endPtr := add(data, mload(data))
            } lt(dataPtr, endPtr) {

            } {
                // Advance 3 bytes
                dataPtr := add(dataPtr, 3)
                let input := mload(dataPtr)

                // To write each character, shift the 3 bytes (18 bits) chunk
                // 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
                // and apply logical AND with 0x3F which is the number of
                // the previous character in the ASCII table prior to the Base64 Table
                // The result is then added to the table to get the character to write,
                // and finally write it in the result pointer but with a left shift
                // of 256 (1 byte) - 8 (1 ASCII char) = 248 bits

                mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance
            }

            // When data `bytes` is not exactly 3 bytes long
            // it is padded with `=` characters at the end
            switch mod(mload(data), 3)
            case 1 {
                mstore8(sub(resultPtr, 1), 0x3d)
                mstore8(sub(resultPtr, 2), 0x3d)
            }
            case 2 {
                mstore8(sub(resultPtr, 1), 0x3d)
            }
        }

        return result;
    }
}

// File: src/Create/interfaces/v1/IMetadataRenderer.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

interface IMetadataRenderer {
    /// @notice Retrieves the token URI for the specified token ID.
    /// @param tokenId The ID of the token.
    /// @return uri The URI of the token.
    function tokenURI(uint256 tokenId) external view returns (string memory uri);
}

// File: src/Create/interfaces/v1/IRandomEditionRenderer.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import {RandomEditionRendererConfiguration} from "create/interfaces/v1/RandomEditionRendererConfiguration.sol";
import {IConfigurable} from "create/interfaces/v1/IConfigurable.sol";

interface IRandomEditionRendererEvents {
    /// @notice Emitted when the configuration is updated for a mint contract.
    /// @param mintContract The address of the contract being configured.
    /// @param config The new configuration.
    event ConfigurationUpdated(address indexed mintContract, RandomEditionRendererConfiguration config);
}

interface IRandomEditionRenderer is IRandomEditionRendererEvents, IConfigurable {
    /// @notice Retrieves the configuration for a contract.
    /// @param mintContract The contract to retreive the configuration for.
    /// @return config The current configuration.
    function configuration(address mintContract)
        external
        view
        returns (RandomEditionRendererConfiguration memory config);
}

// File: src/Create/interfaces/v1/RandomEditionRendererConfiguration.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

struct RandomEditionRendererConfiguration {
    /// @notice The total weight of all the tokens.
    uint32 totalWeight;
    /// @notice The configuration for each token.
    RandomTokenConfiguration[] tokens;
}

struct RandomTokenConfiguration {
    /// @notice The weight of this token if it is selected.
    uint32 weight;
    /// @notice Name of the token
    string tokenName;
    /// @notice Description of the token
    string tokenDescription;
    /// @notice IPFS hash for token's image content
    string imageIPFSHash;
    /// @notice IPFS hash for token's animated content (if any)
    /// If empty, no animated content is associated with the token
    string animationIPFSHash;
    /// @notice Mime type for token's animated content (if any)
    string animationMimeType;
    /// @notice The traits to display on this token.
    TokenTrait[] traits;
}

struct TokenTrait {
    /// @notice The trait type.
    string traitType;
    /// @notice The trait value.
    string traitValue;
}

// File: src/Create/contracts/v1/RandomTokenSelection.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import {
    RandomEditionRendererConfiguration,
    RandomTokenConfiguration
} from "create/interfaces/v1/RandomEditionRendererConfiguration.sol";

library RandomTokenSelection {
    error TokenNotRandomized();
    error Unreachable();

    function getConfiguredToken(RandomEditionRendererConfiguration storage contractConfig, bytes32 random)
        internal
        view
        returns (RandomTokenConfiguration memory)
    {
        if (random == 0) {
            revert TokenNotRandomized();
        }

        uint32 totalWeight = contractConfig.totalWeight;
        uint32 randomWeight = uint32(bytes4(random)) % totalWeight;

        uint32 currentWeight = 0;
        for (uint256 i = 0; i < contractConfig.tokens.length; i++) {
            RandomTokenConfiguration memory tokenConfig = contractConfig.tokens[i];
            currentWeight += tokenConfig.weight;
            if (randomWeight < currentWeight) {
                return tokenConfig;
            }
        }

        revert Unreachable();
    }
}

// File: src/Create/interfaces/v1/IConfigurable.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

interface IConfigurable {
    /// @notice Updates the configuration for the calling contract.
    /// @param data The configuration data.
    function updateConfiguration(bytes calldata data) external;
}

// File: lib/openzeppelin-contracts/contracts/utils/math/Math.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}