ETH Price: $2,557.57 (-0.44%)

Transaction Decoder

Block:
17996736 at Aug-26-2023 05:22:47 AM +UTC
Transaction Fee:
0.000595163100032648 ETH $1.52
Gas Used:
53,263 Gas / 11.174043896 Gwei

Emitted Events:

134 0xd729a94d6366a4feac4a6869c8b3573cee4701a9.0x7c63d8b7131b0fe8af98c14e0ba2b936de7a78faf723c0270d0a2939a4025756( 0x7c63d8b7131b0fe8af98c14e0ba2b936de7a78faf723c0270d0a2939a4025756, 0x0000000000000000000000009bd2607df9c89b223359b08af54bd9c3b42328fd, 0xc3a753db68b924c6a3830c5ba1a6ce9191d5fe1f90624b7f2263b5663abf1deb )

Account State Difference:

  Address   Before After State Difference Code
(Boba Builder)
0.499167606425788843 Eth0.499172932725788843 Eth0.0000053263
0x9bd2607D...3B42328fd
0.014121577595737864 Eth
Nonce: 13
0.013526414495705216 Eth
Nonce: 14
0.000595163100032648
0xD729A94d...cEe4701A9

Execution Trace

0xd729a94d6366a4feac4a6869c8b3573cee4701a9.c3a753db( )
  • EthscriptionsMarketV3.c3a753db( )
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    interface IReentrancyGuard {
        error ReentrancyGuard__ReentrantCall();
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.8;
    import { IReentrancyGuard } from './IReentrancyGuard.sol';
    import { ReentrancyGuardStorage } from './ReentrancyGuardStorage.sol';
    /**
     * @title Utility contract for preventing reentrancy attacks
     */
    abstract contract ReentrancyGuard is IReentrancyGuard {
        uint256 internal constant REENTRANCY_STATUS_LOCKED = 2;
        uint256 internal constant REENTRANCY_STATUS_UNLOCKED = 1;
        modifier nonReentrant() {
            if (ReentrancyGuardStorage.layout().status == REENTRANCY_STATUS_LOCKED)
                revert ReentrancyGuard__ReentrantCall();
            _lockReentrancyGuard();
            _;
            _unlockReentrancyGuard();
        }
        /**
         * @notice lock functions that use the nonReentrant modifier
         */
        function _lockReentrancyGuard() internal virtual {
            ReentrancyGuardStorage.layout().status = REENTRANCY_STATUS_LOCKED;
        }
        /**
         * @notice unlock funtions that use the nonReentrant modifier
         */
        function _unlockReentrancyGuard() internal virtual {
            ReentrancyGuardStorage.layout().status = REENTRANCY_STATUS_UNLOCKED;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.8;
    library ReentrancyGuardStorage {
        struct Layout {
            uint256 status;
        }
        bytes32 internal constant STORAGE_SLOT =
            keccak256('solidstate.contracts.storage.ReentrancyGuard');
        function layout() internal pure returns (Layout storage l) {
            bytes32 slot = STORAGE_SLOT;
            assembly {
                l.slot := slot
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.17;
    library EthscriptionsEscrowerStorage {
        struct Layout {
            mapping(address => mapping(bytes32 => uint256)) ethscriptionReceivedOnBlockNumber;
        }
        bytes32 internal constant STORAGE_SLOT =
            keccak256('ethscriptions.contracts.storage.EthscriptionsEscrowerStorage');
        function s() internal pure returns (Layout storage l) {
            bytes32 slot = STORAGE_SLOT;
            assembly {
                l.slot := slot
            }
        }
    }
    contract EthscriptionsEscrower {
        error EthscriptionNotDeposited();
        error EthscriptionAlreadyReceivedFromSender();
        error InvalidEthscriptionLength();
        error AdditionalCooldownRequired(uint256 additionalBlocksNeeded);
        
        event ethscriptions_protocol_TransferEthscriptionForPreviousOwner(
            address indexed previousOwner,
            address indexed recipient,
            bytes32 indexed id
        );
        
        event PotentialEthscriptionDeposited(
            address indexed owner,
            bytes32 indexed potentialEthscriptionId
        );
        
        event PotentialEthscriptionWithdrawn(
            address indexed owner,
            bytes32 indexed potentialEthscriptionId
        );
        
        uint256 public constant ETHSCRIPTION_TRANSFER_COOLDOWN_BLOCKS = 5;
        
        function _transferEthscription(address previousOwner, address to, bytes32 ethscriptionId) internal virtual {
            _validateTransferEthscription(previousOwner, to, ethscriptionId);
            
            emit ethscriptions_protocol_TransferEthscriptionForPreviousOwner(previousOwner, to, ethscriptionId);
            
            _afterTransferEthscription(previousOwner, to, ethscriptionId);
        }
        
        function withdrawEthscription(bytes32 ethscriptionId) public virtual {
            _transferEthscription(msg.sender, msg.sender, ethscriptionId);
            
            emit PotentialEthscriptionWithdrawn(msg.sender, ethscriptionId);
        }
        
        function _onPotentialEthscriptionDeposit(address previousOwner, bytes calldata userCalldata) internal virtual {
            if (userCalldata.length != 32) revert InvalidEthscriptionLength();
            
            bytes32 potentialEthscriptionId = abi.decode(userCalldata, (bytes32));
            
            if (userEthscriptionPossiblyStored(previousOwner, potentialEthscriptionId)) {
                revert EthscriptionAlreadyReceivedFromSender();
            }
            EthscriptionsEscrowerStorage.s().ethscriptionReceivedOnBlockNumber[previousOwner][potentialEthscriptionId] = block.number;
            
            emit PotentialEthscriptionDeposited(previousOwner, potentialEthscriptionId);
        }
        
        function _validateTransferEthscription(
            address previousOwner,
            address to,
            bytes32 ethscriptionId
        ) internal view virtual {
            if (userEthscriptionDefinitelyNotStored(previousOwner, ethscriptionId)) {
                revert EthscriptionNotDeposited();
            }
            
            uint256 blocksRemaining = blocksRemainingUntilValidTransfer(previousOwner, ethscriptionId);
            
            if (blocksRemaining != 0) {
                revert AdditionalCooldownRequired(blocksRemaining);
            }
        }
        
        function _afterTransferEthscription(
            address previousOwner,
            address to,
            bytes32 ethscriptionId
        ) internal virtual {
            delete EthscriptionsEscrowerStorage.s().ethscriptionReceivedOnBlockNumber[previousOwner][ethscriptionId];
        }
        
        function blocksRemainingUntilValidTransfer(
            address previousOwner,
            bytes32 ethscriptionId
        ) public view virtual returns (uint256) {
            uint256 receivedBlockNumber = EthscriptionsEscrowerStorage.s().ethscriptionReceivedOnBlockNumber[previousOwner][ethscriptionId];
            
            if (receivedBlockNumber == 0) {
                revert EthscriptionNotDeposited();
            }
            
            uint256 blocksPassed = block.number - receivedBlockNumber;
            
            return blocksPassed < ETHSCRIPTION_TRANSFER_COOLDOWN_BLOCKS ?
                ETHSCRIPTION_TRANSFER_COOLDOWN_BLOCKS - blocksPassed :
                0;
        }
        
        function userEthscriptionDefinitelyNotStored(
            address owner,
            bytes32 ethscriptionId
        ) public view virtual returns (bool) {
            return EthscriptionsEscrowerStorage.s().ethscriptionReceivedOnBlockNumber[owner][ethscriptionId] == 0;
        }
        
        function userEthscriptionPossiblyStored(
            address owner,
            bytes32 ethscriptionId
        ) public view virtual returns (bool) {
            return !userEthscriptionDefinitelyNotStored(owner, ethscriptionId);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.17;
    import "solady/src/utils/SafeTransferLib.sol";
    import "solady/src/utils/ECDSA.sol";
    import "solady/src/utils/EIP712.sol";
    import "solady/src/utils/ERC1967FactoryConstants.sol";
    import "@solidstate/contracts/security/reentrancy_guard/ReentrancyGuard.sol";
    import "./EthscriptionsEscrower.sol";
    contract EthscriptionsMarketV3 is ReentrancyGuard, EIP712, EthscriptionsEscrower {
        using SafeTransferLib for address;
        using ECDSA for bytes32;
        
        error NotAdmin();
        error InvalidSignature();
        error AlreadyInitialized();
        error NotFactory();
        error ZeroBalance();
        error ZeroPaymentAddress();
        error ZeroAdminAddress();
        error FeatureDisabled();
        
        event EthscriptionPurchased(
            address indexed seller,
            address indexed buyer,
            bytes32 indexed ethscriptionId,
            uint256 price,
            bytes32 listingId
        );
        
        event ListingCancelled(address indexed seller, bytes32 indexed listingId);
        
        event AllListingsOfEthscriptionCancelledForUser(
            address indexed seller,
            bytes32 indexed ethscriptionId
        );
        
        event AllListingsCancelledForUser(address indexed seller);
        
        event AllListingsCancelled();
        
        event AdminAddressChanged(address indexed oldAdminAddress, address indexed newAdminAddress);
        event PaymentAddressChanged(address indexed oldPaymentAddress, address indexed newPaymentAddress);
        event FeeBpsChanged(uint96 oldFeeBps, uint96 newFeeBps);
        
        event FeesWithdrawn(address indexed recipient, uint256 amount);
        
        struct MarketStorage {
            mapping(address => mapping(bytes32 => bool)) storedEthscriptions;
            mapping(address => mapping(bytes32 => bool)) userListingCancellations;
            mapping(address => mapping(bytes32 => uint256)) userListingsOfEthscriptionValidAfterTime;
            mapping(address => uint256) userListingsValidAfterTime;
            
            address adminAddress;
            address paymentAddress;
            uint96 feeBps;
                
            mapping(string => bool) featureIsEnabled;
        }
        
        function s() internal pure returns (MarketStorage storage cs) {
            bytes32 position = keccak256("MarketStorage.contract.storage.v1");
            assembly {
               cs.slot := position
            }
        }
        
        function initialize(
            address adminAddress,
            address paymentAddress,
            uint96 feeBps,
            address[] calldata importOwners,
            bytes32[] calldata importHashes
        ) external {
            if (msg.sender != ERC1967FactoryConstants.ADDRESS) revert NotFactory();
            if (paymentAddress == address(0)) revert ZeroPaymentAddress();
            if (adminAddress == address(0)) revert ZeroAdminAddress();
            
            s().adminAddress = adminAddress;
            s().paymentAddress = paymentAddress;
            
            s().featureIsEnabled['withdraw'] = true;
            
            s().feeBps = feeBps;
            
            uint256 length = importOwners.length;
            for (uint256 i; i < length;) {
                EthscriptionsEscrowerStorage.s().ethscriptionReceivedOnBlockNumber[importOwners[i]][importHashes[i]] = block.number;
                unchecked {
                    i++;
                }
            }
        }
        
        function buyWithSignature(
            bytes32 listingId,
            address seller,
            bytes32 ethscriptionId,
            uint256 price,
            uint256 startTime,
            uint256 endTime,
            bytes calldata signature
        ) external payable nonReentrant {
            if (!s().featureIsEnabled['buy']) revert FeatureDisabled();
            
            bytes32 hashedMessage = _hashTypedData(keccak256(abi.encode(
                keccak256(
                    "Listing(bytes32 listingId,address seller,bytes32 ethscriptionId,"
                    "uint256 price,uint256 startTime,uint256 endTime)"
                ),
                listingId,
                seller,
                ethscriptionId,
                price,
                startTime,
                endTime
            )));
            
            address signer = hashedMessage.recoverCalldata(signature);
            if (
                signer != seller ||
                block.timestamp < startTime ||
                block.timestamp > endTime ||
                msg.value != price ||
                s().userListingCancellations[seller][listingId] ||
                startTime <= s().userListingsOfEthscriptionValidAfterTime[seller][ethscriptionId] ||
                startTime <= s().userListingsValidAfterTime[seller]
            ) {
                revert InvalidSignature();
            }
                   
            seller.forceSafeTransferETH(price - computeFee(price));
            
            _transferEthscription(seller, msg.sender, ethscriptionId);
            
            emit EthscriptionPurchased(seller, msg.sender, ethscriptionId, price, listingId);
        }
        
        function withdrawEthscription(bytes32 ethscriptionId) public override {
            if (!s().featureIsEnabled['withdraw']) revert FeatureDisabled();
            
            super.withdrawEthscription(ethscriptionId);
        }
        
        function _onPotentialEthscriptionDeposit(address previousOwner, bytes calldata userCalldata) internal override {
            if (!s().featureIsEnabled['deposit']) revert FeatureDisabled();
            
            super._onPotentialEthscriptionDeposit(previousOwner, userCalldata);
        }
        
        function _afterTransferEthscription(address previousOwner, address to, bytes32 ethscriptionId) internal override {
            s().userListingsOfEthscriptionValidAfterTime[previousOwner][ethscriptionId] = block.timestamp;
            
            super._afterTransferEthscription(previousOwner, to, ethscriptionId);
        }
        
        function cancelListing(bytes32 listingId) external {
            s().userListingCancellations[msg.sender][listingId] = true;
            emit ListingCancelled(msg.sender, listingId);
        }
        
        function cancelAllListingsForEthscription(bytes32 ethscriptionId) external {
            s().userListingsOfEthscriptionValidAfterTime[msg.sender][ethscriptionId] = block.timestamp;
            emit AllListingsOfEthscriptionCancelledForUser(msg.sender, ethscriptionId);
        }
        
        function cancelAllListingsOfUser() external {
            s().userListingsValidAfterTime[msg.sender] = block.timestamp;
            emit AllListingsCancelledForUser(msg.sender);
        }
        
        function setAdminAddress(address adminAddress) external {
            if (msg.sender != s().adminAddress) revert NotAdmin();
            if (adminAddress == address(0)) revert ZeroAdminAddress();
            emit AdminAddressChanged(s().adminAddress, adminAddress);
            s().adminAddress = adminAddress;
        }
        
        function setPaymentAddress(address paymentAddress) external {
            if (msg.sender != s().adminAddress) revert NotAdmin();
            if (paymentAddress == address(0)) revert ZeroPaymentAddress();
            
            emit PaymentAddressChanged(s().paymentAddress, paymentAddress);
            
            s().paymentAddress = paymentAddress;
        }
        
        function setFeeBps(uint96 feeBps) external {
            if (msg.sender != s().adminAddress) revert NotAdmin();
            
            emit FeeBpsChanged(s().feeBps, feeBps);
            
            s().feeBps = feeBps;
        }
        
        function sendFeesToPaymentAddress() external {
            if (msg.sender != s().adminAddress) revert NotAdmin();
            if (address(this).balance == 0) revert ZeroBalance();
            if (s().paymentAddress == address(0)) revert ZeroPaymentAddress();
            
            emit FeesWithdrawn(s().paymentAddress, address(this).balance);
            
            s().paymentAddress.forceSafeTransferETH(address(this).balance);
        }
        
        function setFeatureStatus(string memory feature, bool enabled) internal {
            if (msg.sender != s().adminAddress) revert NotAdmin();
            
            s().featureIsEnabled[feature] = enabled;
        }
        
        function enableFeature(string memory feature) public {
            if (msg.sender != s().adminAddress) revert NotAdmin();
            setFeatureStatus(feature, true);
        }
        function disableFeature(string memory feature) public {
            if (msg.sender != s().adminAddress) revert NotAdmin();
            setFeatureStatus(feature, false);
        }
        function enableAllFeatures() external {
            if (msg.sender != s().adminAddress) revert NotAdmin();
            enableFeature("buy");
            enableFeature("deposit");
            enableFeature("withdraw");
        }
        
        function disableAllFeatures() external {
            if (msg.sender != s().adminAddress) revert NotAdmin();
            disableFeature("buy");
            disableFeature("deposit");
            disableFeature("withdraw");
        }
        
        function featureIsEnabled(string calldata feature) external view returns (bool) {
            return s().featureIsEnabled[feature];
        }
        
        function computeFee(uint256 amount) public view returns (uint256) {
            return (amount * s().feeBps) / 10000;
        }
        
        function getFeeBps() external view returns (uint256) {
            return s().feeBps;
        }
        
        function userListingCancellations(address owner, bytes32 listingId) external view returns (bool) {
            return s().userListingCancellations[owner][listingId];
        }
        function userListingsOfEthscriptionValidAfterTime(address owner, bytes32 ethscriptionId) external view returns (uint256) {
            return s().userListingsOfEthscriptionValidAfterTime[owner][ethscriptionId];
        }
        function userListingsValidAfterTime(address owner) external view returns (uint256) {
            return s().userListingsValidAfterTime[owner];
        }
        
        fallback() external {
            _onPotentialEthscriptionDeposit(msg.sender, msg.data);
        }
        function _domainNameAndVersion() 
            internal
            pure
            override
            returns (string memory name, string memory version)
        {
            name = "Ethscriptions Market";
            version = "3";
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    /// @notice Gas optimized ECDSA wrapper.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol)
    /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol)
    /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol)
    library ECDSA {
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                        CUSTOM ERRORS                       */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev The signature is invalid.
        error InvalidSignature();
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                         CONSTANTS                          */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev The number which `s` must be less than in order for
        /// the signature to be non-malleable.
        bytes32 private constant _MALLEABILITY_THRESHOLD_PLUS_ONE =
            0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1;
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                    RECOVERY OPERATIONS                     */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        // Note: as of Solady version 0.0.68, these functions will
        // revert upon recovery failure for more safety by default.
        /// @dev Recovers the signer's address from a message digest `hash`,
        /// and the `signature`.
        ///
        /// This function does NOT accept EIP-2098 short form signatures.
        /// Use `recover(bytes32 hash, bytes32 r, bytes32 vs)` for EIP-2098
        /// short form signatures instead.
        function recover(bytes32 hash, bytes memory signature) internal view returns (address result) {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                mstore(0x40, mload(add(signature, 0x20))) // `r`.
                mstore(0x60, mload(add(signature, 0x40))) // `s`.
                pop(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        and(
                            // If the signature is exactly 65 bytes in length.
                            eq(mload(signature), 65),
                            // If `s` in lower half order, such that the signature is not malleable.
                            lt(mload(0x60), _MALLEABILITY_THRESHOLD_PLUS_ONE)
                        ), // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x00, // Start of output.
                        0x20 // Size of output.
                    )
                )
                result := mload(0x00)
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if iszero(returndatasize()) {
                    mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                    revert(0x1c, 0x04)
                }
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
        /// @dev Recovers the signer's address from a message digest `hash`,
        /// and the `signature`.
        ///
        /// This function does NOT accept EIP-2098 short form signatures.
        /// Use `recover(bytes32 hash, bytes32 r, bytes32 vs)` for EIP-2098
        /// short form signatures instead.
        function recoverCalldata(bytes32 hash, bytes calldata signature)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                pop(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        and(
                            // If the signature is exactly 65 bytes in length.
                            eq(signature.length, 65),
                            // If `s` in lower half order, such that the signature is not malleable.
                            lt(mload(0x60), _MALLEABILITY_THRESHOLD_PLUS_ONE)
                        ), // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x00, // Start of output.
                        0x20 // Size of output.
                    )
                )
                result := mload(0x00)
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if iszero(returndatasize()) {
                    mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                    revert(0x1c, 0x04)
                }
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
        /// @dev Recovers the signer's address from a message digest `hash`,
        /// and the EIP-2098 short form signature defined by `r` and `vs`.
        ///
        /// This function only accepts EIP-2098 short form signatures.
        /// See: https://eips.ethereum.org/EIPS/eip-2098
        function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                mstore(0x20, add(shr(255, vs), 27)) // `v`.
                mstore(0x40, r)
                mstore(0x60, shr(1, shl(1, vs))) // `s`.
                pop(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        // If `s` in lower half order, such that the signature is not malleable.
                        lt(mload(0x60), _MALLEABILITY_THRESHOLD_PLUS_ONE), // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x00, // Start of output.
                        0x20 // Size of output.
                    )
                )
                result := mload(0x00)
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if iszero(returndatasize()) {
                    mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                    revert(0x1c, 0x04)
                }
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
        /// @dev Recovers the signer's address from a message digest `hash`,
        /// and the signature defined by `v`, `r`, `s`.
        function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                mstore(0x20, and(v, 0xff))
                mstore(0x40, r)
                mstore(0x60, s)
                pop(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        // If `s` in lower half order, such that the signature is not malleable.
                        lt(s, _MALLEABILITY_THRESHOLD_PLUS_ONE), // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x00, // Start of output.
                        0x20 // Size of output.
                    )
                )
                result := mload(0x00)
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if iszero(returndatasize()) {
                    mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                    revert(0x1c, 0x04)
                }
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                   TRY-RECOVER OPERATIONS                   */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        // WARNING!
        // These functions will NOT revert upon recovery failure.
        // Instead, they will return the zero address upon recovery failure.
        // It is critical that the returned address is NEVER compared against
        // a zero address (e.g. an uninitialized address variable).
        /// @dev Recovers the signer's address from a message digest `hash`,
        /// and the `signature`.
        ///
        /// This function does NOT accept EIP-2098 short form signatures.
        /// Use `recover(bytes32 hash, bytes32 r, bytes32 vs)` for EIP-2098
        /// short form signatures instead.
        function tryRecover(bytes32 hash, bytes memory signature)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                mstore(0x40, mload(add(signature, 0x20))) // `r`.
                mstore(0x60, mload(add(signature, 0x40))) // `s`.
                pop(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        and(
                            // If the signature is exactly 65 bytes in length.
                            eq(mload(signature), 65),
                            // If `s` in lower half order, such that the signature is not malleable.
                            lt(mload(0x60), _MALLEABILITY_THRESHOLD_PLUS_ONE)
                        ), // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x40, // Start of output.
                        0x20 // Size of output.
                    )
                )
                mstore(0x60, 0) // Restore the zero slot.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                result := mload(xor(0x60, returndatasize()))
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
        /// @dev Recovers the signer's address from a message digest `hash`,
        /// and the `signature`.
        ///
        /// This function does NOT accept EIP-2098 short form signatures.
        /// Use `recover(bytes32 hash, bytes32 r, bytes32 vs)` for EIP-2098
        /// short form signatures instead.
        function tryRecoverCalldata(bytes32 hash, bytes calldata signature)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                pop(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        and(
                            // If the signature is exactly 65 bytes in length.
                            eq(signature.length, 65),
                            // If `s` in lower half order, such that the signature is not malleable.
                            lt(mload(0x60), _MALLEABILITY_THRESHOLD_PLUS_ONE)
                        ), // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x40, // Start of output.
                        0x20 // Size of output.
                    )
                )
                mstore(0x60, 0) // Restore the zero slot.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                result := mload(xor(0x60, returndatasize()))
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
        /// @dev Recovers the signer's address from a message digest `hash`,
        /// and the EIP-2098 short form signature defined by `r` and `vs`.
        ///
        /// This function only accepts EIP-2098 short form signatures.
        /// See: https://eips.ethereum.org/EIPS/eip-2098
        function tryRecover(bytes32 hash, bytes32 r, bytes32 vs)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                mstore(0x20, add(shr(255, vs), 27)) // `v`.
                mstore(0x40, r)
                mstore(0x60, shr(1, shl(1, vs))) // `s`.
                pop(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        // If `s` in lower half order, such that the signature is not malleable.
                        lt(mload(0x60), _MALLEABILITY_THRESHOLD_PLUS_ONE), // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x40, // Start of output.
                        0x20 // Size of output.
                    )
                )
                mstore(0x60, 0) // Restore the zero slot.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                result := mload(xor(0x60, returndatasize()))
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
        /// @dev Recovers the signer's address from a message digest `hash`,
        /// and the signature defined by `v`, `r`, `s`.
        function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                mstore(0x20, and(v, 0xff))
                mstore(0x40, r)
                mstore(0x60, s)
                pop(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        // If `s` in lower half order, such that the signature is not malleable.
                        lt(s, _MALLEABILITY_THRESHOLD_PLUS_ONE), // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x40, // Start of output.
                        0x20 // Size of output.
                    )
                )
                mstore(0x60, 0) // Restore the zero slot.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                result := mload(xor(0x60, returndatasize()))
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                     HASHING OPERATIONS                     */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev Returns an Ethereum Signed Message, created from a `hash`.
        /// This produces a hash corresponding to the one signed with the
        /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
        /// JSON-RPC method as part of EIP-191.
        function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0x20, hash) // Store into scratch space for keccak256.
                mstore(0x00, "\\x00\\x00\\x00\\x00\\x19Ethereum Signed Message:\
    32") // 28 bytes.
                result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
            }
        }
        /// @dev Returns an Ethereum Signed Message, created from `s`.
        /// This produces a hash corresponding to the one signed with the
        /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
        /// JSON-RPC method as part of EIP-191.
        /// Note: Supports lengths of `s` up to 999999 bytes.
        function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
            /// @solidity memory-safe-assembly
            assembly {
                let sLength := mload(s)
                let o := 0x20
                mstore(o, "\\x19Ethereum Signed Message:\
    ") // 26 bytes, zero-right-padded.
                mstore(0x00, 0x00)
                // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
                for { let temp := sLength } 1 {} {
                    o := sub(o, 1)
                    mstore8(o, add(48, mod(temp, 10)))
                    temp := div(temp, 10)
                    if iszero(temp) { break }
                }
                let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
                // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
                returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
                mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
                result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
                mstore(s, sLength) // Restore the length.
            }
        }
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                   EMPTY CALLDATA HELPERS                   */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev Returns an empty calldata bytes.
        function emptySignature() internal pure returns (bytes calldata signature) {
            /// @solidity memory-safe-assembly
            assembly {
                signature.length := 0
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    /// @notice Contract for EIP-712 typed structured data hashing and signing.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EIP712.sol)
    /// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/utils/EIP712.sol)
    /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol)
    /// Note, this implementation:
    /// - Uses `address(this)` for the `verifyingContract` field.
    /// - Does NOT use the optional EIP-712 salt.
    /// - Does NOT use any EIP-712 extensions.
    /// This is for simplicity and to save gas.
    /// If you need to customize, please fork / modify accordingly.
    abstract contract EIP712 {
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                  CONSTANTS AND IMMUTABLES                  */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
        bytes32 internal constant _DOMAIN_TYPEHASH =
            0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
        address private immutable _cachedThis;
        uint256 private immutable _cachedChainId;
        bytes32 private immutable _cachedNameHash;
        bytes32 private immutable _cachedVersionHash;
        bytes32 private immutable _cachedDomainSeparator;
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                        CONSTRUCTOR                         */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev Cache the hashes for cheaper runtime gas costs.
        /// In the case of upgradeable contracts (i.e. proxies),
        /// or if the chain id changes due to a hard fork,
        /// the domain separator will be seamlessly calculated on-the-fly.
        constructor() {
            _cachedThis = address(this);
            _cachedChainId = block.chainid;
            (string memory name, string memory version) = _domainNameAndVersion();
            bytes32 nameHash = keccak256(bytes(name));
            bytes32 versionHash = keccak256(bytes(version));
            _cachedNameHash = nameHash;
            _cachedVersionHash = versionHash;
            bytes32 separator;
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Load the free memory pointer.
                mstore(m, _DOMAIN_TYPEHASH)
                mstore(add(m, 0x20), nameHash)
                mstore(add(m, 0x40), versionHash)
                mstore(add(m, 0x60), chainid())
                mstore(add(m, 0x80), address())
                separator := keccak256(m, 0xa0)
            }
            _cachedDomainSeparator = separator;
        }
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                   FUNCTIONS TO OVERRIDE                    */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev Please override this function to return the domain name and version.
        /// ```
        ///     function _domainNameAndVersion()
        ///         internal
        ///         pure
        ///         virtual
        ///         returns (string memory name, string memory version)
        ///     {
        ///         name = "Solady";
        ///         version = "1";
        ///     }
        /// ```
        function _domainNameAndVersion()
            internal
            pure
            virtual
            returns (string memory name, string memory version);
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                     HASHING OPERATIONS                     */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev Returns the EIP-712 domain separator.
        function _domainSeparator() internal view virtual returns (bytes32 separator) {
            separator = _cachedDomainSeparator;
            if (_cachedDomainSeparatorInvalidated()) {
                separator = _buildDomainSeparator();
            }
        }
        /// @dev Returns the hash of the fully encoded EIP-712 message for this domain,
        /// given `structHash`, as defined in
        /// https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
        ///
        /// The hash can be used together with {ECDSA-recover} to obtain the signer of a message:
        /// ```
        ///     bytes32 digest = _hashTypedData(keccak256(abi.encode(
        ///         keccak256("Mail(address to,string contents)"),
        ///         mailTo,
        ///         keccak256(bytes(mailContents))
        ///     )));
        ///     address signer = ECDSA.recover(digest, signature);
        /// ```
        function _hashTypedData(bytes32 structHash) internal view virtual returns (bytes32 digest) {
            bytes32 separator = _cachedDomainSeparator;
            if (_cachedDomainSeparatorInvalidated()) {
                separator = _buildDomainSeparator();
            }
            /// @solidity memory-safe-assembly
            assembly {
                // Compute the digest.
                mstore(0x00, 0x1901000000000000) // Store "\\x19\\x01".
                mstore(0x1a, separator) // Store the domain separator.
                mstore(0x3a, structHash) // Store the struct hash.
                digest := keccak256(0x18, 0x42)
                // Restore the part of the free memory slot that was overwritten.
                mstore(0x3a, 0)
            }
        }
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                    EIP-5267 OPERATIONS                     */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev See: https://eips.ethereum.org/EIPS/eip-5267
        function eip712Domain()
            public
            view
            virtual
            returns (
                bytes1 fields,
                string memory name,
                string memory version,
                uint256 chainId,
                address verifyingContract,
                bytes32 salt,
                uint256[] memory extensions
            )
        {
            fields = hex"0f"; // `0b01111`.
            (name, version) = _domainNameAndVersion();
            chainId = block.chainid;
            verifyingContract = address(this);
            salt = salt; // `bytes32(0)`.
            extensions = extensions; // `new uint256[](0)`.
        }
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                      PRIVATE HELPERS                       */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev Returns the EIP-712 domain separator.
        function _buildDomainSeparator() private view returns (bytes32 separator) {
            bytes32 nameHash = _cachedNameHash;
            bytes32 versionHash = _cachedVersionHash;
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Load the free memory pointer.
                mstore(m, _DOMAIN_TYPEHASH)
                mstore(add(m, 0x20), nameHash)
                mstore(add(m, 0x40), versionHash)
                mstore(add(m, 0x60), chainid())
                mstore(add(m, 0x80), address())
                separator := keccak256(m, 0xa0)
            }
        }
        /// @dev Returns if the cached domain separator has been invalidated.
        function _cachedDomainSeparatorInvalidated() private view returns (bool result) {
            uint256 cachedChainId = _cachedChainId;
            address cachedThis = _cachedThis;
            /// @solidity memory-safe-assembly
            assembly {
                result := iszero(and(eq(chainid(), cachedChainId), eq(address(), cachedThis)))
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    /// @notice The address and bytecode of the canonical ERC1967Factory deployment.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ERC1967FactoryLib.sol)
    /// @author jtriley-eth (https://github.com/jtriley-eth/minimum-viable-proxy)
    ///
    /// @dev The canonical ERC1967Factory is deployed permissionlessly via
    /// 0age's ImmutableCreate2Factory located at 0x0000000000FFe8B47B3e2130213B802212439497.
    ///
    /// `ADDRESS = immutableCreate2Factory.safeCreate2(SALT, INITCODE)`
    ///
    /// If the canonical ERC1967Factory has not been deployed on your EVM chain of choice,
    /// please feel free to deploy via 0age's ImmutableCreate2Factory.
    ///
    /// If 0age's ImmutableCreate2Factory has not been deployed on your EVM chain of choice,
    /// please refer to 0age's ImmutableCreate2Factory deployment instructions at:
    /// https://github.com/ProjectOpenSea/seaport/blob/main/docs/Deployment.md
    ///
    /// Contract verification can be done either via command line or block explorers like Etherscan.
    /// The simplest and most reliable way is via block explorer with single file input.
    library ERC1967FactoryConstants {
        /// @dev The canonical ERC1967Factory address for EVM chains.
        address internal constant ADDRESS = 0x0000000000006396FF2a80c067f99B3d2Ab4Df24;
        /// @dev The canonical ERC1967Factory bytecode for EVM chains.
        /// Useful for forge tests:
        /// `vm.etch(ADDRESS, BYTECODE)`.
        bytes internal constant BYTECODE =
            hex"6080604052600436106100b15760003560e01c8063545e7c611161006957806399a88ec41161004e57806399a88ec41461019d578063a97b90d5146101b0578063db4c545e146101c357600080fd5b8063545e7c61146101775780639623609d1461018a57600080fd5b80633729f9221161009a5780633729f922146101315780634314f120146101445780635414dff01461015757600080fd5b80631acfd02a146100b65780632abbef15146100d8575b600080fd5b3480156100c257600080fd5b506100d66100d1366004610604565b6101e6565b005b3480156100e457600080fd5b506101076100f3366004610637565b30600c908152600091909152602090205490565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b61010761013f366004610652565b610237565b6101076101523660046106d7565b61024e565b34801561016357600080fd5b50610107610172366004610738565b610267565b610107610185366004610604565b61029a565b6100d66101983660046106d7565b6102af565b6100d66101ab366004610604565b61035f565b6101076101be366004610751565b610370565b3480156101cf57600080fd5b506101d86103a9565b604051908152602001610128565b30600c52816000526020600c2033815414610209576382b429006000526004601cfd5b81905580827f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f600080a35050565b60006102468484843685610370565b949350505050565b600061025e8585838087876103c2565b95945050505050565b6000806102726103a9565b905060ff600053806035523060601b6001528260155260556000209150600060355250919050565b60006102a88383368461024e565b9392505050565b30600c5283600052336020600c2054146102d1576382b429006000526004601cfd5b6040518381527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc602082015281836040830137600080836040018334895af1610331573d610327576355299b496000526004601cfd5b3d6000803e3d6000fd5b5082847f5d611f318680d00598bb735d61bacf0c514c6b50e1e5ad30040a4df2b12791c7600080a350505050565b61036c82823660006102af565b5050565b60008360601c33148460601c151761039057632f6348366000526004601cfd5b61039f868686600187876103c2565b9695505050505050565b6000806103b461049c565b608960139091012092915050565b6000806103cd61049c565b90508480156103e757866089601384016000f592506103f3565b6089601383016000f092505b50816104075763301164256000526004601cfd5b8781527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc602082015282846040830137600080846040018334865af161045a573d6103275763301164256000526004601cfd5b30600c5281600052866020600c20558688837fc95935a66d15e0da5e412aca0ad27ae891d20b2fb91cf3994b6a3bf2b8178082600080a4509695505050505050565b6040513060701c801561054257666052573d6000fd607b8301527f3d356020355560408036111560525736038060403d373d3d355af43d6000803e60748301527f3735a920a3ca505d382bbc545af43d6000803e6052573d6000fd5b3d6000f35b60548301527f14605757363d3d37363d7f360894a13ba1a3210667c828492db98dca3e2076cc60348301523060148301526c607f3d8160093d39f33d3d337382525090565b66604c573d6000fd60758301527f3d3560203555604080361115604c5736038060403d373d3d355af43d6000803e606e8301527f3735a920a3ca505d382bbc545af43d6000803e604c573d6000fd5b3d6000f35b604e8301527f14605157363d3d37363d7f360894a13ba1a3210667c828492db98dca3e2076cc602e83015230600e8301526c60793d8160093d39f33d3d336d82525090565b803573ffffffffffffffffffffffffffffffffffffffff811681146105ff57600080fd5b919050565b6000806040838503121561061757600080fd5b610620836105db565b915061062e602084016105db565b90509250929050565b60006020828403121561064957600080fd5b6102a8826105db565b60008060006060848603121561066757600080fd5b610670846105db565b925061067e602085016105db565b9150604084013590509250925092565b60008083601f8401126106a057600080fd5b50813567ffffffffffffffff8111156106b857600080fd5b6020830191508360208285010111156106d057600080fd5b9250929050565b600080600080606085870312156106ed57600080fd5b6106f6856105db565b9350610704602086016105db565b9250604085013567ffffffffffffffff81111561072057600080fd5b61072c8782880161068e565b95989497509550505050565b60006020828403121561074a57600080fd5b5035919050565b60008060008060006080868803121561076957600080fd5b610772866105db565b9450610780602087016105db565b935060408601359250606086013567ffffffffffffffff8111156107a357600080fd5b6107af8882890161068e565b96999598509396509294939250505056fea26469706673582212200ac7c3ccbc2d311c48bf5465b021542e0e306fe3c462c060ba6a3d2f81ff6c5f64736f6c63430008130033";
        /// @dev The initcode used to deploy the canonical ERC1967Factory.
        bytes internal constant INITCODE = abi.encodePacked(
            hex"608060405234801561001057600080fd5b506107f6806100206000396000f3fe", BYTECODE
        );
        /// @dev For deterministic deployment via 0age's ImmutableCreate2Factory.
        bytes32 internal constant SALT =
            0x0000000000000000000000000000000000000000e75e4f228818c80007508f33;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
    /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
    ///
    /// @dev Note:
    /// - For ETH transfers, please use `forceSafeTransferETH` for gas griefing protection.
    /// - For ERC20s, this implementation won't check that a token has code,
    /// responsibility is delegated to the caller.
    library SafeTransferLib {
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                       CUSTOM ERRORS                        */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev The ETH transfer has failed.
        error ETHTransferFailed();
        /// @dev The ERC20 `transferFrom` has failed.
        error TransferFromFailed();
        /// @dev The ERC20 `transfer` has failed.
        error TransferFailed();
        /// @dev The ERC20 `approve` has failed.
        error ApproveFailed();
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                         CONSTANTS                          */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev Suggested gas stipend for contract receiving ETH
        /// that disallows any storage writes.
        uint256 internal constant _GAS_STIPEND_NO_STORAGE_WRITES = 2300;
        /// @dev Suggested gas stipend for contract receiving ETH to perform a few
        /// storage reads and writes, but low enough to prevent griefing.
        /// Multiply by a small constant (e.g. 2), if needed.
        uint256 internal constant _GAS_STIPEND_NO_GRIEF = 100000;
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                       ETH OPERATIONS                       */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev Sends `amount` (in wei) ETH to `to`.
        /// Reverts upon failure.
        ///
        /// Note: This implementation does NOT protect against gas griefing.
        /// Please use `forceSafeTransferETH` for gas griefing protection.
        function safeTransferETH(address to, uint256 amount) internal {
            /// @solidity memory-safe-assembly
            assembly {
                // Transfer the ETH and check if it succeeded or not.
                if iszero(call(gas(), to, amount, 0, 0, 0, 0)) {
                    // Store the function selector of `ETHTransferFailed()`.
                    mstore(0x00, 0xb12d13eb)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
            }
        }
        /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
        /// The `gasStipend` can be set to a low enough value to prevent
        /// storage writes or gas griefing.
        ///
        /// If sending via the normal procedure fails, force sends the ETH by
        /// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH.
        ///
        /// Reverts if the current contract has insufficient balance.
        function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
            /// @solidity memory-safe-assembly
            assembly {
                // If insufficient balance, revert.
                if lt(selfbalance(), amount) {
                    // Store the function selector of `ETHTransferFailed()`.
                    mstore(0x00, 0xb12d13eb)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                // Transfer the ETH and check if it succeeded or not.
                if iszero(call(gasStipend, to, amount, 0, 0, 0, 0)) {
                    mstore(0x00, to) // Store the address in scratch space.
                    mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                    mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                    // We can directly use `SELFDESTRUCT` in the contract creation.
                    // Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758
                    if iszero(create(amount, 0x0b, 0x16)) {
                        // To coerce gas estimation to provide enough gas for the `create` above.
                        if iszero(gt(gas(), 1000000)) { revert(0, 0) }
                    }
                }
            }
        }
        /// @dev Force sends `amount` (in wei) ETH to `to`, with a gas stipend
        /// equal to `_GAS_STIPEND_NO_GRIEF`. This gas stipend is a reasonable default
        /// for 99% of cases and can be overridden with the three-argument version of this
        /// function if necessary.
        ///
        /// If sending via the normal procedure fails, force sends the ETH by
        /// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH.
        ///
        /// Reverts if the current contract has insufficient balance.
        function forceSafeTransferETH(address to, uint256 amount) internal {
            // Manually inlined because the compiler doesn't inline functions with branches.
            /// @solidity memory-safe-assembly
            assembly {
                // If insufficient balance, revert.
                if lt(selfbalance(), amount) {
                    // Store the function selector of `ETHTransferFailed()`.
                    mstore(0x00, 0xb12d13eb)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                // Transfer the ETH and check if it succeeded or not.
                if iszero(call(_GAS_STIPEND_NO_GRIEF, to, amount, 0, 0, 0, 0)) {
                    mstore(0x00, to) // Store the address in scratch space.
                    mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                    mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                    // We can directly use `SELFDESTRUCT` in the contract creation.
                    // Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758
                    if iszero(create(amount, 0x0b, 0x16)) {
                        // To coerce gas estimation to provide enough gas for the `create` above.
                        if iszero(gt(gas(), 1000000)) { revert(0, 0) }
                    }
                }
            }
        }
        /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
        /// The `gasStipend` can be set to a low enough value to prevent
        /// storage writes or gas griefing.
        ///
        /// Simply use `gasleft()` for `gasStipend` if you don't need a gas stipend.
        ///
        /// Note: Does NOT revert upon failure.
        /// Returns whether the transfer of ETH is successful instead.
        function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
            internal
            returns (bool success)
        {
            /// @solidity memory-safe-assembly
            assembly {
                // Transfer the ETH and check if it succeeded or not.
                success := call(gasStipend, to, amount, 0, 0, 0, 0)
            }
        }
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                      ERC20 OPERATIONS                      */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
        /// Reverts upon failure.
        ///
        /// The `from` account must have at least `amount` approved for
        /// the current contract to manage.
        function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x60, amount) // Store the `amount` argument.
                mstore(0x40, to) // Store the `to` argument.
                mstore(0x2c, shl(96, from)) // Store the `from` argument.
                // Store the function selector of `transferFrom(address,address,uint256)`.
                mstore(0x0c, 0x23b872dd000000000000000000000000)
                if iszero(
                    and( // The arguments of `and` are evaluated from right to left.
                        // Set success to whether the call reverted, if not we check it either
                        // returned exactly 1 (can't just be non-zero data), or had no return data.
                        or(eq(mload(0x00), 1), iszero(returndatasize())),
                        call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                    )
                ) {
                    // Store the function selector of `TransferFromFailed()`.
                    mstore(0x00, 0x7939f424)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                mstore(0x60, 0) // Restore the zero slot to zero.
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
        /// @dev Sends all of ERC20 `token` from `from` to `to`.
        /// Reverts upon failure.
        ///
        /// The `from` account must have their entire balance approved for
        /// the current contract to manage.
        function safeTransferAllFrom(address token, address from, address to)
            internal
            returns (uint256 amount)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x40, to) // Store the `to` argument.
                mstore(0x2c, shl(96, from)) // Store the `from` argument.
                // Store the function selector of `balanceOf(address)`.
                mstore(0x0c, 0x70a08231000000000000000000000000)
                if iszero(
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                    )
                ) {
                    // Store the function selector of `TransferFromFailed()`.
                    mstore(0x00, 0x7939f424)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                // Store the function selector of `transferFrom(address,address,uint256)`.
                mstore(0x00, 0x23b872dd)
                // The `amount` argument is already written to the memory word at 0x60.
                amount := mload(0x60)
                if iszero(
                    and( // The arguments of `and` are evaluated from right to left.
                        // Set success to whether the call reverted, if not we check it either
                        // returned exactly 1 (can't just be non-zero data), or had no return data.
                        or(eq(mload(0x00), 1), iszero(returndatasize())),
                        call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                    )
                ) {
                    // Store the function selector of `TransferFromFailed()`.
                    mstore(0x00, 0x7939f424)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                mstore(0x60, 0) // Restore the zero slot to zero.
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
        /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
        /// Reverts upon failure.
        function safeTransfer(address token, address to, uint256 amount) internal {
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0x14, to) // Store the `to` argument.
                mstore(0x34, amount) // Store the `amount` argument.
                // Store the function selector of `transfer(address,uint256)`.
                mstore(0x00, 0xa9059cbb000000000000000000000000)
                if iszero(
                    and( // The arguments of `and` are evaluated from right to left.
                        // Set success to whether the call reverted, if not we check it either
                        // returned exactly 1 (can't just be non-zero data), or had no return data.
                        or(eq(mload(0x00), 1), iszero(returndatasize())),
                        call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    )
                ) {
                    // Store the function selector of `TransferFailed()`.
                    mstore(0x00, 0x90b8ec18)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                // Restore the part of the free memory pointer that was overwritten.
                mstore(0x34, 0)
            }
        }
        /// @dev Sends all of ERC20 `token` from the current contract to `to`.
        /// Reverts upon failure.
        function safeTransferAll(address token, address to) internal returns (uint256 amount) {
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
                mstore(0x20, address()) // Store the address of the current contract.
                if iszero(
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                    )
                ) {
                    // Store the function selector of `TransferFailed()`.
                    mstore(0x00, 0x90b8ec18)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                mstore(0x14, to) // Store the `to` argument.
                // The `amount` argument is already written to the memory word at 0x34.
                amount := mload(0x34)
                // Store the function selector of `transfer(address,uint256)`.
                mstore(0x00, 0xa9059cbb000000000000000000000000)
                if iszero(
                    and( // The arguments of `and` are evaluated from right to left.
                        // Set success to whether the call reverted, if not we check it either
                        // returned exactly 1 (can't just be non-zero data), or had no return data.
                        or(eq(mload(0x00), 1), iszero(returndatasize())),
                        call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    )
                ) {
                    // Store the function selector of `TransferFailed()`.
                    mstore(0x00, 0x90b8ec18)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                // Restore the part of the free memory pointer that was overwritten.
                mstore(0x34, 0)
            }
        }
        /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
        /// Reverts upon failure.
        function safeApprove(address token, address to, uint256 amount) internal {
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0x14, to) // Store the `to` argument.
                mstore(0x34, amount) // Store the `amount` argument.
                // Store the function selector of `approve(address,uint256)`.
                mstore(0x00, 0x095ea7b3000000000000000000000000)
                if iszero(
                    and( // The arguments of `and` are evaluated from right to left.
                        // Set success to whether the call reverted, if not we check it either
                        // returned exactly 1 (can't just be non-zero data), or had no return data.
                        or(eq(mload(0x00), 1), iszero(returndatasize())),
                        call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    )
                ) {
                    // Store the function selector of `ApproveFailed()`.
                    mstore(0x00, 0x3e3f8f73)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                // Restore the part of the free memory pointer that was overwritten.
                mstore(0x34, 0)
            }
        }
        /// @dev Returns the amount of ERC20 `token` owned by `account`.
        /// Returns zero if the `token` does not exist.
        function balanceOf(address token, address account) internal view returns (uint256 amount) {
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0x14, account) // Store the `account` argument.
                // Store the function selector of `balanceOf(address)`.
                mstore(0x00, 0x70a08231000000000000000000000000)
                amount :=
                    mul(
                        mload(0x20),
                        and( // The arguments of `and` are evaluated from right to left.
                            gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                            staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                        )
                    )
            }
        }
    }