ETH Price: $2,287.14 (-5.95%)

Transaction Decoder

Block:
18297837 at Oct-07-2023 09:53:47 AM +UTC
Transaction Fee:
0.000492863495484042 ETH $1.13
Gas Used:
84,618 Gas / 5.824570369 Gwei

Emitted Events:

66 0x965b0c68b67a702f125421fe435ff8ebb87cd088.0xf1d95ed4d1680e6f665104f19c296ae52c1f64cd8114e84d55dc6349dbdafea3( 0xf1d95ed4d1680e6f665104f19c296ae52c1f64cd8114e84d55dc6349dbdafea3, 0x000000000000000000000000fb62118a35691141a248d77ce7acfd3de007a1e9, 0x000000000000000000000000627da04a45d28127cfb04d9407e08d968f4832bf, 0x805ffe0fc8b48a23321c5e3da7aade387e7c9f2ca343406ecbc84a7f99438753 )
67 0x965b0c68b67a702f125421fe435ff8ebb87cd088.0x0c8ec4ea3fc0ad639ce6d4d303428a6e6ecb4d513819f4f4f22189dfa7341294( 0x0c8ec4ea3fc0ad639ce6d4d303428a6e6ecb4d513819f4f4f22189dfa7341294, 0x000000000000000000000000fb62118a35691141a248d77ce7acfd3de007a1e9, 0x000000000000000000000000627da04a45d28127cfb04d9407e08d968f4832bf, 0x805ffe0fc8b48a23321c5e3da7aade387e7c9f2ca343406ecbc84a7f99438753, 0000000000000000000000000000000000000000000000000019396991e7c000, c1d14a80e2ba8c57f04f80402ea273b4f073bbb6521eafad9649c6288bfbd26b )

Account State Difference:

  Address   Before After State Difference Code
0x627dA04a...68F4832bf
0.068650333261942537 Eth
Nonce: 3
0.061057469766458495 Eth
Nonce: 4
0.007592863495484042
(Fee Recipient: 0x8b02...030)
9.55113076404545669 Eth9.55113922584545669 Eth0.0000084618
0x965B0c68...Bb87Cd088 0.0014894 Eth0.0016314 Eth0.000142
0xfB62118a...dE007a1E9 0.000981958385158998 Eth0.007939958385158998 Eth0.006958

Execution Trace

ETH 0.0071 0x965b0c68b67a702f125421fe435ff8ebb87cd088.e61fb866( )
  • ETH 0.0071 EthscriptionsMarketV3.buyWithSignature( listingId=C1D14A80E2BA8C57F04F80402EA273B4F073BBB6521EAFAD9649C6288BFBD26B, seller=0xfB62118a35691141A248d77ce7aCFd3dE007a1E9, ethscriptionId=805FFE0FC8B48A23321C5E3DA7AADE387E7C9F2CA343406ECBC84A7F99438753, price=7100000000000000, startTime=1691461184, endTime=1707358740, signature=0x0EA9BBB9064B322CE72B093E6F4E9EFD1609E20FE1D728CD4E24EB9418DC2F692BBAA944F9F26427A4DC2F63D602830710FE26E80D1F4B4CA57DFC9AF5AAB6BF1B )
    • Null: 0x000...001.be97980a( )
    • ETH 0.006958 0xfb62118a35691141a248d77ce7acfd3de007a1e9.CALL( )
      // 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)
                          )
                      )
              }
          }
      }