ETH Price: $2,060.21 (-0.22%)

Transaction Decoder

Block:
20423752 at Jul-31-2024 03:09:35 AM +UTC
Transaction Fee:
0.000162245144202075 ETH $0.33
Gas Used:
93,543 Gas / 1.734444525 Gwei

Emitted Events:

420 0xd729345aa12c5af2121d96f87b673987f354496b.0x665fba0baf3dc33e9943340197893ac16f56482c2defb8de60f944987fee451c( 0x665fba0baf3dc33e9943340197893ac16f56482c2defb8de60f944987fee451c, 0x00000000000000000000000000000000000000000000000000000000000face7, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000115, 646174613a6170706c69636174696f6e2f766e642e66616365742e74782b6a73, 6f6e3b72756c653d65736970362c7b226f70223a2263616c6c222c2264617461, 223a7b22746f223a223078313637333534303234336537393362306537376330, 33386434613838343438656666353234646365222c2266756e6374696f6e223a, 226d61726b5769746864726177616c436f6d706c657465222c2261726773223a, 5b22307866623731343364623236346430613235666361343737373261333562, 353266343630353037326135222c223078353839373932653239623834313163, 3762626333633138396330626463636264323463646436373363613030336339, 6161613665353731643964316262663164225d7d7d0000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0xD729345a...7f354496B 730.678972302506133159 Eth730.661221328823192734 Eth0.017750973682940425
0xFb7143Db...4605072a5
0.056250537657965812 Eth
Nonce: 171
0.073839266196704162 Eth
Nonce: 172
0.01758872853873835

Execution Trace

0xd729345aa12c5af2121d96f87b673987f354496b.072278c8( )
  • FacetEtherBridgeV4.withdraw( req=[{name:recipient, type:address, order:1, indexed:false, value:0xFb7143Db264D0A25fCA47772a35B52F4605072a5, valueString:0xFb7143Db264D0A25fCA47772a35B52F4605072a5}, {name:amount, type:uint256, order:2, indexed:false, value:17750973682940425, valueString:17750973682940425}, {name:withdrawalId, type:bytes32, order:3, indexed:false, value:589792E29B8411C7BBC3C189C0BDCCBD24CDD673CA003C9AAA6E571D9D1BBF1D, valueString:589792E29B8411C7BBC3C189C0BDCCBD24CDD673CA003C9AAA6E571D9D1BBF1D}, {name:blockHash, type:bytes32, order:4, indexed:false, value:54364A45A1742B14E3B54F8705C5F01D39884AF12C6D4CC8350ED9D83B0A5D8B, valueString:54364A45A1742B14E3B54F8705C5F01D39884AF12C6D4CC8350ED9D83B0A5D8B}, {name:blockNumber, type:uint256, order:5, indexed:false, value:20423709, valueString:20423709}, {name:signature, type:bytes, order:6, indexed:false, value:0xC0C9907667D9C38D2A6A095E3CFD33C72C41621EFDF1F79A5C2CCB28D4519B4F2403E77435C6C634777D694FF35ACA3F81DFB8FF3842426C261BE1CDA4BCE43E1B, valueString:0xC0C9907667D9C38D2A6A095E3CFD33C72C41621EFDF1F79A5C2CCB28D4519B4F2403E77435C6C634777D694FF35ACA3F81DFB8FF3842426C261BE1CDA4BCE43E1B}] )
    • Null: 0x000...001.bb9c5d22( )
    • ETH 0.017750973682940425 0xfb7143db264d0a25fca47772a35b52f4605072a5.CALL( )
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.18;
      import "solady/src/utils/SafeTransferLib.sol";
      import "solady/src/utils/ECDSA.sol";
      import "solady/src/utils/EIP712.sol";
      import "solady/src/utils/Base64.sol";
      import "solady/src/utils/LibString.sol";
      import "solady/src/utils/ERC1967FactoryConstants.sol";
      import "@solidstate/contracts/security/reentrancy_guard/ReentrancyGuard.sol";
      contract FacetEtherBridgeV4 is ReentrancyGuard, EIP712 {
          using LibString for *;
          using SafeTransferLib for address;
          using ECDSA for bytes32;
          error FeatureDisabled();
          error InvalidAmount();
          error NotFactory();
          error ZeroAdminAddress();
          event ethscriptions_protocol_CreateEthscription(
              address indexed initialOwner,
              string contentURI
          );
          struct WithdrawRequest {
              address recipient;
              uint256 amount;
              bytes32 withdrawalId;
              bytes32 blockHash;
              uint256 blockNumber;
              bytes signature;
          }
          
          struct BridgeStorage {
              mapping(bytes32 => bool) processedWithdraws;
              bool depositEnabled;
              bool withdrawEnabled;
              address adminAddress;
              address signerAddress;
              address dumbContractAddress;
              uint256 cancelBlockNumber;
              uint256 withdrawDelay;
          }
          
          function s() internal pure returns (BridgeStorage storage cs) {
              bytes32 position = keccak256("BridgeStorage.contract.storage.v1");
              assembly {
                 cs.slot := position
              }
          }
          
          modifier onlyAdmin() {
              require(msg.sender == s().adminAddress, "Not admin");
              _;
          }
          
          function initialize(
              address adminAddress
          ) external {
              require(msg.sender == ERC1967FactoryConstants.ADDRESS, "Not factory");
              require(s().adminAddress == address(0), "Already initialized");
              require(adminAddress != address(0), "No zero address");
              s().adminAddress = adminAddress;
          }
          
          function deposit() public payable {
              require(msg.sender == tx.origin, "Only EOAs can use this bridge");
              
              uint256 amount = msg.value;
              address recipient = msg.sender;
              if (amount == 0) revert InvalidAmount();
              
              string memory out = string.concat(
                  'data:application/vnd.facet.tx+json;rule=esip6,{"op":"call","data":{"to":"',
                  _hardCodedDumbContractAddressString(),
                  '","function":"bridgeIn","args":["',
                  recipient.toHexString(), '","',
                  amount.toString(), '"]}}'
              );
              
              emit ethscriptions_protocol_CreateEthscription(0x00000000000000000000000000000000000FacE7, out);
          }
          
          function bridgeAndCall(
              address recipient,
              address dumbContractToCall,
              string calldata functionCalldata
          ) external payable {
              require(msg.sender == tx.origin, "Only EOAs can use this bridge");
              uint256 amount = msg.value;
              if (amount == 0) revert InvalidAmount();
                      
              string memory out = string.concat(
                  'data:application/vnd.facet.tx+json;rule=esip6,{"op":"call","data":{"to":"',
                  _hardCodedDumbContractAddressString(),
                  '","function":"bridgeAndCall","args":["',
                  recipient.toHexString(), '","',
                  amount.toString(), '","',
                  dumbContractToCall.toHexString(), '","',
                  Base64.encode(bytes(functionCalldata)), '"]}}'
              );
              emit ethscriptions_protocol_CreateEthscription(0x00000000000000000000000000000000000FacE7, out);
          }
          
          function withdraw(
              WithdrawRequest calldata req
          ) external nonReentrant {
              require(msg.sender == tx.origin, "Only EOAs can use this bridge");
              require(s().withdrawEnabled, "Withdraw disabled");
              bytes32 hashedMessage = _hashTypedData(keccak256(abi.encode(
                  keccak256(
                      "Withdraw(address recipient,address dumbContract,uint256 amount,"
                      "bytes32 withdrawalId,bytes32 blockHash,uint256 blockNumber)"
                  ),
                  req.recipient,
                  s().dumbContractAddress,
                  req.amount,
                  req.withdrawalId,
                  req.blockHash,
                  req.blockNumber
              )));
              address signer = hashedMessage.recoverCalldata(req.signature);
              require(signer == s().signerAddress, "Invalid signature");
              require(!s().processedWithdraws[req.withdrawalId], "Already processed");
              require(s().cancelBlockNumber <= req.blockNumber, "Signature canceled");
              require(block.number >= req.blockNumber + s().withdrawDelay, "Withdraw delay");
              require(req.blockHash == bytes32(0) || blockhash(req.blockNumber) == req.blockHash, "Invalid block number or hash");
              s().processedWithdraws[req.withdrawalId] = true;
              req.recipient.forceSafeTransferETH(req.amount);
              string memory out = string.concat(
                  'data:application/vnd.facet.tx+json;rule=esip6,{"op":"call","data":{"to":"',
                  _hardCodedDumbContractAddressString(),
                  '","function":"markWithdrawalComplete","args":["',
                  req.recipient.toHexString(), '","',
                  uint256(req.withdrawalId).toHexString(32), '"]}}'
              );
              
              emit ethscriptions_protocol_CreateEthscription(0x00000000000000000000000000000000000FacE7, string(out));
          }
          
          receive() external payable {
              deposit();
          }
          
          function adminMarkComplete(address recipient, bytes32 withdrawalId) external onlyAdmin {
              s().processedWithdraws[withdrawalId] = true;
              
              string memory out = string.concat(
                  'data:application/vnd.facet.tx+json;rule=esip6,{"op":"call","data":{"to":"',
                  s().dumbContractAddress.toHexString(),
                  '","function":"markWithdrawalComplete","args":{"to":"',
                  recipient.toHexString(),
                  '","withdrawalId":"',
                  uint256(withdrawalId).toHexString(32),
                  '"}}}'
              );
              
              emit ethscriptions_protocol_CreateEthscription(0x00000000000000000000000000000000000FacE7, string(out));
          }
          
          function _hardCodedDumbContractAddressString() internal pure returns (string memory) {
              return "0x1673540243e793b0e77c038d4a88448eff524dce";
          }
          
          function setWithdrawEnabled(bool enabled) external onlyAdmin {
              s().withdrawEnabled = enabled;
          }
          
          function setDepositEnabled(bool enabled) external onlyAdmin {
              s().depositEnabled = enabled;
          }
          function enableAllFeatures() external onlyAdmin {
              s().depositEnabled = true;
              s().withdrawEnabled = true;
          }
          
          function disableAllFeatures() external onlyAdmin {
              s().depositEnabled = false;
              s().withdrawEnabled = false;
          }
          
          function cancelSignatures() external onlyAdmin {
              s().cancelBlockNumber = block.number;
          }
          
          function setDumbContract(address dumbContract) external onlyAdmin {
              s().dumbContractAddress = dumbContract;
          }
          
          function setSigner(address signer) external onlyAdmin {
              s().signerAddress = signer;
          }
          
          function setAdmin(address admin) external onlyAdmin {
              s().adminAddress = admin;
          }
          
          function setWithdrawDelay(uint256 withdrawDelay) external onlyAdmin {
              s().withdrawDelay = withdrawDelay;
          }
          
          function getSigner() external view returns (address) {
              return s().signerAddress;
          }
          
          function getAdmin() external view returns (address) {
              return s().adminAddress;
          }
          
          function getWithdrawDelay() external view returns (uint256) {
              return s().withdrawDelay;
          }
          
          function getDumbContract() external view returns (address) {
              return s().dumbContractAddress;
          }
          
          function getHardCodedDumbContractAddressString() external pure returns (string memory) {
              return _hardCodedDumbContractAddressString();
          }
          
          function getCancelBlockNumber() external view returns (uint256) {
              return s().cancelBlockNumber;
          }
          
          function getDepositEnabled() external view returns (bool) {
              return s().depositEnabled;
          }
          
          function getWithdrawalEnabled() external view returns (bool) {
              return s().depositEnabled;
          }
          
          function processedWithdraws(bytes32 withdrawalId) external view returns (bool) {
              return s().processedWithdraws[withdrawalId];
          }
          function _domainNameAndVersion() 
              internal
              pure
              override
              returns (string memory name, string memory version)
          {
              name = "Facet Ether Bridge";
              version = "1";
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.18;
      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() virtual {
              if (_isReentrancyGuardLocked()) revert ReentrancyGuard__ReentrantCall();
              _lockReentrancyGuard();
              _;
              _unlockReentrancyGuard();
          }
          /**
           * @notice returns true if the reentrancy guard is locked, false otherwise
           */
          function _isReentrancyGuardLocked() internal view virtual returns (bool) {
              return
                  ReentrancyGuardStorage.layout().status == REENTRANCY_STATUS_LOCKED;
          }
          /**
           * @notice lock functions that use the nonReentrant modifier
           */
          function _lockReentrancyGuard() internal virtual {
              ReentrancyGuardStorage.layout().status = REENTRANCY_STATUS_LOCKED;
          }
          /**
           * @notice unlock functions that use the nonReentrant modifier
           */
          function _unlockReentrancyGuard() internal virtual {
              ReentrancyGuardStorage.layout().status = REENTRANCY_STATUS_UNLOCKED;
          }
      }
      // 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:
      /// - Source code:
      ///   https://github.com/Vectorized/solady/blob/5212e50fef1f2ff1b1b5e03a5d276a0d23c02713/src/utils/ERC1967Factory.sol
      ///   (The EXACT source code is required. Use the file at the commit instead of the latest copy.)
      /// - Optimization Enabled: Yes with 1000000 runs
      /// - Compiler Version: v0.8.19+commit.7dd6d404
      /// - Other Settings: default evmVersion, MIT license
      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 Library for converting numbers into strings and other string operations.
      /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
      /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
      ///
      /// @dev Note:
      /// For performance and bytecode compactness, most of the string operations are restricted to
      /// byte strings (7-bit ASCII), except where otherwise specified.
      /// Usage of byte string operations on charsets with runes spanning two or more bytes
      /// can lead to undefined behavior.
      library LibString {
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                        CUSTOM ERRORS                       */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The length of the output is too small to contain all the hex digits.
          error HexLengthInsufficient();
          /// @dev The length of the string is more than 32 bytes.
          error TooBigForSmallString();
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                         CONSTANTS                          */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The constant returned when the `search` is not found in the string.
          uint256 internal constant NOT_FOUND = type(uint256).max;
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                     DECIMAL OPERATIONS                     */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Returns the base 10 decimal representation of `value`.
          function toString(uint256 value) internal pure returns (string memory str) {
              /// @solidity memory-safe-assembly
              assembly {
                  // The maximum value of a uint256 contains 78 digits (1 byte per digit), but
                  // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
                  // We will need 1 word for the trailing zeros padding, 1 word for the length,
                  // and 3 words for a maximum of 78 digits.
                  str := add(mload(0x40), 0x80)
                  // Update the free memory pointer to allocate.
                  mstore(0x40, add(str, 0x20))
                  // Zeroize the slot after the string.
                  mstore(str, 0)
                  // Cache the end of the memory to calculate the length later.
                  let end := str
                  let w := not(0) // Tsk.
                  // We write the string from rightmost digit to leftmost digit.
                  // The following is essentially a do-while loop that also handles the zero case.
                  for { let temp := value } 1 {} {
                      str := add(str, w) // `sub(str, 1)`.
                      // Write the character to the pointer.
                      // The ASCII index of the '0' character is 48.
                      mstore8(str, add(48, mod(temp, 10)))
                      // Keep dividing `temp` until zero.
                      temp := div(temp, 10)
                      if iszero(temp) { break }
                  }
                  let length := sub(end, str)
                  // Move the pointer 32 bytes leftwards to make room for the length.
                  str := sub(str, 0x20)
                  // Store the length.
                  mstore(str, length)
              }
          }
          /// @dev Returns the base 10 decimal representation of `value`.
          function toString(int256 value) internal pure returns (string memory str) {
              if (value >= 0) {
                  return toString(uint256(value));
              }
              unchecked {
                  str = toString(~uint256(value) + 1);
              }
              /// @solidity memory-safe-assembly
              assembly {
                  // We still have some spare memory space on the left,
                  // as we have allocated 3 words (96 bytes) for up to 78 digits.
                  let length := mload(str) // Load the string length.
                  mstore(str, 0x2d) // Store the '-' character.
                  str := sub(str, 1) // Move back the string pointer by a byte.
                  mstore(str, add(length, 1)) // Update the string length.
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                   HEXADECIMAL OPERATIONS                   */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Returns the hexadecimal representation of `value`,
          /// left-padded to an input length of `length` bytes.
          /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
          /// giving a total length of `length * 2 + 2` bytes.
          /// Reverts if `length` is too small for the output to contain all the digits.
          function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {
              str = toHexStringNoPrefix(value, length);
              /// @solidity memory-safe-assembly
              assembly {
                  let strLength := add(mload(str), 2) // Compute the length.
                  mstore(str, 0x3078) // Write the "0x" prefix.
                  str := sub(str, 2) // Move the pointer.
                  mstore(str, strLength) // Write the length.
              }
          }
          /// @dev Returns the hexadecimal representation of `value`,
          /// left-padded to an input length of `length` bytes.
          /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
          /// giving a total length of `length * 2` bytes.
          /// Reverts if `length` is too small for the output to contain all the digits.
          function toHexStringNoPrefix(uint256 value, uint256 length)
              internal
              pure
              returns (string memory str)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes
                  // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
                  // We add 0x20 to the total and round down to a multiple of 0x20.
                  // (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
                  str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))
                  // Allocate the memory.
                  mstore(0x40, add(str, 0x20))
                  // Zeroize the slot after the string.
                  mstore(str, 0)
                  // Cache the end to calculate the length later.
                  let end := str
                  // Store "0123456789abcdef" in scratch space.
                  mstore(0x0f, 0x30313233343536373839616263646566)
                  let start := sub(str, add(length, length))
                  let w := not(1) // Tsk.
                  let temp := value
                  // We write the string from rightmost digit to leftmost digit.
                  // The following is essentially a do-while loop that also handles the zero case.
                  for {} 1 {} {
                      str := add(str, w) // `sub(str, 2)`.
                      mstore8(add(str, 1), mload(and(temp, 15)))
                      mstore8(str, mload(and(shr(4, temp), 15)))
                      temp := shr(8, temp)
                      if iszero(xor(str, start)) { break }
                  }
                  if temp {
                      mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.
                      revert(0x1c, 0x04)
                  }
                  // Compute the string's length.
                  let strLength := sub(end, str)
                  // Move the pointer and write the length.
                  str := sub(str, 0x20)
                  mstore(str, strLength)
              }
          }
          /// @dev Returns the hexadecimal representation of `value`.
          /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
          /// As address are 20 bytes long, the output will left-padded to have
          /// a length of `20 * 2 + 2` bytes.
          function toHexString(uint256 value) internal pure returns (string memory str) {
              str = toHexStringNoPrefix(value);
              /// @solidity memory-safe-assembly
              assembly {
                  let strLength := add(mload(str), 2) // Compute the length.
                  mstore(str, 0x3078) // Write the "0x" prefix.
                  str := sub(str, 2) // Move the pointer.
                  mstore(str, strLength) // Write the length.
              }
          }
          /// @dev Returns the hexadecimal representation of `value`.
          /// The output is prefixed with "0x".
          /// The output excludes leading "0" from the `toHexString` output.
          /// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`.
          function toMinimalHexString(uint256 value) internal pure returns (string memory str) {
              str = toHexStringNoPrefix(value);
              /// @solidity memory-safe-assembly
              assembly {
                  let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
                  let strLength := add(mload(str), 2) // Compute the length.
                  mstore(add(str, o), 0x3078) // Write the "0x" prefix, accounting for leading zero.
                  str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero.
                  mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.
              }
          }
          /// @dev Returns the hexadecimal representation of `value`.
          /// The output excludes leading "0" from the `toHexStringNoPrefix` output.
          /// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`.
          function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
              str = toHexStringNoPrefix(value);
              /// @solidity memory-safe-assembly
              assembly {
                  let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
                  let strLength := mload(str) // Get the length.
                  str := add(str, o) // Move the pointer, accounting for leading zero.
                  mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.
              }
          }
          /// @dev Returns the hexadecimal representation of `value`.
          /// The output is encoded using 2 hexadecimal digits per byte.
          /// As address are 20 bytes long, the output will left-padded to have
          /// a length of `20 * 2` bytes.
          function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
              /// @solidity memory-safe-assembly
              assembly {
                  // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
                  // 0x02 bytes for the prefix, and 0x40 bytes for the digits.
                  // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
                  str := add(mload(0x40), 0x80)
                  // Allocate the memory.
                  mstore(0x40, add(str, 0x20))
                  // Zeroize the slot after the string.
                  mstore(str, 0)
                  // Cache the end to calculate the length later.
                  let end := str
                  // Store "0123456789abcdef" in scratch space.
                  mstore(0x0f, 0x30313233343536373839616263646566)
                  let w := not(1) // Tsk.
                  // We write the string from rightmost digit to leftmost digit.
                  // The following is essentially a do-while loop that also handles the zero case.
                  for { let temp := value } 1 {} {
                      str := add(str, w) // `sub(str, 2)`.
                      mstore8(add(str, 1), mload(and(temp, 15)))
                      mstore8(str, mload(and(shr(4, temp), 15)))
                      temp := shr(8, temp)
                      if iszero(temp) { break }
                  }
                  // Compute the string's length.
                  let strLength := sub(end, str)
                  // Move the pointer and write the length.
                  str := sub(str, 0x20)
                  mstore(str, strLength)
              }
          }
          /// @dev Returns the hexadecimal representation of `value`.
          /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
          /// and the alphabets are capitalized conditionally according to
          /// https://eips.ethereum.org/EIPS/eip-55
          function toHexStringChecksummed(address value) internal pure returns (string memory str) {
              str = toHexString(value);
              /// @solidity memory-safe-assembly
              assembly {
                  let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
                  let o := add(str, 0x22)
                  let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
                  let t := shl(240, 136) // `0b10001000 << 240`
                  for { let i := 0 } 1 {} {
                      mstore(add(i, i), mul(t, byte(i, hashed)))
                      i := add(i, 1)
                      if eq(i, 20) { break }
                  }
                  mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
                  o := add(o, 0x20)
                  mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
              }
          }
          /// @dev Returns the hexadecimal representation of `value`.
          /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
          function toHexString(address value) internal pure returns (string memory str) {
              str = toHexStringNoPrefix(value);
              /// @solidity memory-safe-assembly
              assembly {
                  let strLength := add(mload(str), 2) // Compute the length.
                  mstore(str, 0x3078) // Write the "0x" prefix.
                  str := sub(str, 2) // Move the pointer.
                  mstore(str, strLength) // Write the length.
              }
          }
          /// @dev Returns the hexadecimal representation of `value`.
          /// The output is encoded using 2 hexadecimal digits per byte.
          function toHexStringNoPrefix(address value) internal pure returns (string memory str) {
              /// @solidity memory-safe-assembly
              assembly {
                  str := mload(0x40)
                  // Allocate the memory.
                  // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
                  // 0x02 bytes for the prefix, and 0x28 bytes for the digits.
                  // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
                  mstore(0x40, add(str, 0x80))
                  // Store "0123456789abcdef" in scratch space.
                  mstore(0x0f, 0x30313233343536373839616263646566)
                  str := add(str, 2)
                  mstore(str, 40)
                  let o := add(str, 0x20)
                  mstore(add(o, 40), 0)
                  value := shl(96, value)
                  // We write the string from rightmost digit to leftmost digit.
                  // The following is essentially a do-while loop that also handles the zero case.
                  for { let i := 0 } 1 {} {
                      let p := add(o, add(i, i))
                      let temp := byte(i, value)
                      mstore8(add(p, 1), mload(and(temp, 15)))
                      mstore8(p, mload(shr(4, temp)))
                      i := add(i, 1)
                      if eq(i, 20) { break }
                  }
              }
          }
          /// @dev Returns the hex encoded string from the raw bytes.
          /// The output is encoded using 2 hexadecimal digits per byte.
          function toHexString(bytes memory raw) internal pure returns (string memory str) {
              str = toHexStringNoPrefix(raw);
              /// @solidity memory-safe-assembly
              assembly {
                  let strLength := add(mload(str), 2) // Compute the length.
                  mstore(str, 0x3078) // Write the "0x" prefix.
                  str := sub(str, 2) // Move the pointer.
                  mstore(str, strLength) // Write the length.
              }
          }
          /// @dev Returns the hex encoded string from the raw bytes.
          /// The output is encoded using 2 hexadecimal digits per byte.
          function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {
              /// @solidity memory-safe-assembly
              assembly {
                  let length := mload(raw)
                  str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
                  mstore(str, add(length, length)) // Store the length of the output.
                  // Store "0123456789abcdef" in scratch space.
                  mstore(0x0f, 0x30313233343536373839616263646566)
                  let o := add(str, 0x20)
                  let end := add(raw, length)
                  for {} iszero(eq(raw, end)) {} {
                      raw := add(raw, 1)
                      mstore8(add(o, 1), mload(and(mload(raw), 15)))
                      mstore8(o, mload(and(shr(4, mload(raw)), 15)))
                      o := add(o, 2)
                  }
                  mstore(o, 0) // Zeroize the slot after the string.
                  mstore(0x40, add(o, 0x20)) // Allocate the memory.
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                   RUNE STRING OPERATIONS                   */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Returns the number of UTF characters in the string.
          function runeCount(string memory s) internal pure returns (uint256 result) {
              /// @solidity memory-safe-assembly
              assembly {
                  if mload(s) {
                      mstore(0x00, div(not(0), 255))
                      mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
                      let o := add(s, 0x20)
                      let end := add(o, mload(s))
                      for { result := 1 } 1 { result := add(result, 1) } {
                          o := add(o, byte(0, mload(shr(250, mload(o)))))
                          if iszero(lt(o, end)) { break }
                      }
                  }
              }
          }
          /// @dev Returns if this string is a 7-bit ASCII string.
          /// (i.e. all characters codes are in [0..127])
          function is7BitASCII(string memory s) internal pure returns (bool result) {
              /// @solidity memory-safe-assembly
              assembly {
                  let mask := shl(7, div(not(0), 255))
                  result := 1
                  let n := mload(s)
                  if n {
                      let o := add(s, 0x20)
                      let end := add(o, n)
                      let last := mload(end)
                      mstore(end, 0)
                      for {} 1 {} {
                          if and(mask, mload(o)) {
                              result := 0
                              break
                          }
                          o := add(o, 0x20)
                          if iszero(lt(o, end)) { break }
                      }
                      mstore(end, last)
                  }
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                   BYTE STRING OPERATIONS                   */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          // For performance and bytecode compactness, byte string operations are restricted
          // to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.
          // Usage of byte string operations on charsets with runes spanning two or more bytes
          // can lead to undefined behavior.
          /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`.
          function replace(string memory subject, string memory search, string memory replacement)
              internal
              pure
              returns (string memory result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  let subjectLength := mload(subject)
                  let searchLength := mload(search)
                  let replacementLength := mload(replacement)
                  subject := add(subject, 0x20)
                  search := add(search, 0x20)
                  replacement := add(replacement, 0x20)
                  result := add(mload(0x40), 0x20)
                  let subjectEnd := add(subject, subjectLength)
                  if iszero(gt(searchLength, subjectLength)) {
                      let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)
                      let h := 0
                      if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
                      let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
                      let s := mload(search)
                      for {} 1 {} {
                          let t := mload(subject)
                          // Whether the first `searchLength % 32` bytes of
                          // `subject` and `search` matches.
                          if iszero(shr(m, xor(t, s))) {
                              if h {
                                  if iszero(eq(keccak256(subject, searchLength), h)) {
                                      mstore(result, t)
                                      result := add(result, 1)
                                      subject := add(subject, 1)
                                      if iszero(lt(subject, subjectSearchEnd)) { break }
                                      continue
                                  }
                              }
                              // Copy the `replacement` one word at a time.
                              for { let o := 0 } 1 {} {
                                  mstore(add(result, o), mload(add(replacement, o)))
                                  o := add(o, 0x20)
                                  if iszero(lt(o, replacementLength)) { break }
                              }
                              result := add(result, replacementLength)
                              subject := add(subject, searchLength)
                              if searchLength {
                                  if iszero(lt(subject, subjectSearchEnd)) { break }
                                  continue
                              }
                          }
                          mstore(result, t)
                          result := add(result, 1)
                          subject := add(subject, 1)
                          if iszero(lt(subject, subjectSearchEnd)) { break }
                      }
                  }
                  let resultRemainder := result
                  result := add(mload(0x40), 0x20)
                  let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))
                  // Copy the rest of the string one word at a time.
                  for {} lt(subject, subjectEnd) {} {
                      mstore(resultRemainder, mload(subject))
                      resultRemainder := add(resultRemainder, 0x20)
                      subject := add(subject, 0x20)
                  }
                  result := sub(result, 0x20)
                  let last := add(add(result, 0x20), k) // Zeroize the slot after the string.
                  mstore(last, 0)
                  mstore(0x40, add(last, 0x20)) // Allocate the memory.
                  mstore(result, k) // Store the length.
              }
          }
          /// @dev Returns the byte index of the first location of `search` in `subject`,
          /// searching from left to right, starting from `from`.
          /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
          function indexOf(string memory subject, string memory search, uint256 from)
              internal
              pure
              returns (uint256 result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  for { let subjectLength := mload(subject) } 1 {} {
                      if iszero(mload(search)) {
                          if iszero(gt(from, subjectLength)) {
                              result := from
                              break
                          }
                          result := subjectLength
                          break
                      }
                      let searchLength := mload(search)
                      let subjectStart := add(subject, 0x20)
                      result := not(0) // Initialize to `NOT_FOUND`.
                      subject := add(subjectStart, from)
                      let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)
                      let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
                      let s := mload(add(search, 0x20))
                      if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }
                      if iszero(lt(searchLength, 0x20)) {
                          for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
                              if iszero(shr(m, xor(mload(subject), s))) {
                                  if eq(keccak256(subject, searchLength), h) {
                                      result := sub(subject, subjectStart)
                                      break
                                  }
                              }
                              subject := add(subject, 1)
                              if iszero(lt(subject, end)) { break }
                          }
                          break
                      }
                      for {} 1 {} {
                          if iszero(shr(m, xor(mload(subject), s))) {
                              result := sub(subject, subjectStart)
                              break
                          }
                          subject := add(subject, 1)
                          if iszero(lt(subject, end)) { break }
                      }
                      break
                  }
              }
          }
          /// @dev Returns the byte index of the first location of `search` in `subject`,
          /// searching from left to right.
          /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
          function indexOf(string memory subject, string memory search)
              internal
              pure
              returns (uint256 result)
          {
              result = indexOf(subject, search, 0);
          }
          /// @dev Returns the byte index of the first location of `search` in `subject`,
          /// searching from right to left, starting from `from`.
          /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
          function lastIndexOf(string memory subject, string memory search, uint256 from)
              internal
              pure
              returns (uint256 result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  for {} 1 {} {
                      result := not(0) // Initialize to `NOT_FOUND`.
                      let searchLength := mload(search)
                      if gt(searchLength, mload(subject)) { break }
                      let w := result
                      let fromMax := sub(mload(subject), searchLength)
                      if iszero(gt(fromMax, from)) { from := fromMax }
                      let end := add(add(subject, 0x20), w)
                      subject := add(add(subject, 0x20), from)
                      if iszero(gt(subject, end)) { break }
                      // As this function is not too often used,
                      // we shall simply use keccak256 for smaller bytecode size.
                      for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
                          if eq(keccak256(subject, searchLength), h) {
                              result := sub(subject, add(end, 1))
                              break
                          }
                          subject := add(subject, w) // `sub(subject, 1)`.
                          if iszero(gt(subject, end)) { break }
                      }
                      break
                  }
              }
          }
          /// @dev Returns the byte index of the first location of `search` in `subject`,
          /// searching from right to left.
          /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
          function lastIndexOf(string memory subject, string memory search)
              internal
              pure
              returns (uint256 result)
          {
              result = lastIndexOf(subject, search, uint256(int256(-1)));
          }
          /// @dev Returns true if `search` is found in `subject`, false otherwise.
          function contains(string memory subject, string memory search) internal pure returns (bool) {
              return indexOf(subject, search) != NOT_FOUND;
          }
          /// @dev Returns whether `subject` starts with `search`.
          function startsWith(string memory subject, string memory search)
              internal
              pure
              returns (bool result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  let searchLength := mload(search)
                  // Just using keccak256 directly is actually cheaper.
                  // forgefmt: disable-next-item
                  result := and(
                      iszero(gt(searchLength, mload(subject))),
                      eq(
                          keccak256(add(subject, 0x20), searchLength),
                          keccak256(add(search, 0x20), searchLength)
                      )
                  )
              }
          }
          /// @dev Returns whether `subject` ends with `search`.
          function endsWith(string memory subject, string memory search)
              internal
              pure
              returns (bool result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  let searchLength := mload(search)
                  let subjectLength := mload(subject)
                  // Whether `search` is not longer than `subject`.
                  let withinRange := iszero(gt(searchLength, subjectLength))
                  // Just using keccak256 directly is actually cheaper.
                  // forgefmt: disable-next-item
                  result := and(
                      withinRange,
                      eq(
                          keccak256(
                              // `subject + 0x20 + max(subjectLength - searchLength, 0)`.
                              add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),
                              searchLength
                          ),
                          keccak256(add(search, 0x20), searchLength)
                      )
                  )
              }
          }
          /// @dev Returns `subject` repeated `times`.
          function repeat(string memory subject, uint256 times)
              internal
              pure
              returns (string memory result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  let subjectLength := mload(subject)
                  if iszero(or(iszero(times), iszero(subjectLength))) {
                      subject := add(subject, 0x20)
                      result := mload(0x40)
                      let output := add(result, 0x20)
                      for {} 1 {} {
                          // Copy the `subject` one word at a time.
                          for { let o := 0 } 1 {} {
                              mstore(add(output, o), mload(add(subject, o)))
                              o := add(o, 0x20)
                              if iszero(lt(o, subjectLength)) { break }
                          }
                          output := add(output, subjectLength)
                          times := sub(times, 1)
                          if iszero(times) { break }
                      }
                      mstore(output, 0) // Zeroize the slot after the string.
                      let resultLength := sub(output, add(result, 0x20))
                      mstore(result, resultLength) // Store the length.
                      // Allocate the memory.
                      mstore(0x40, add(result, add(resultLength, 0x20)))
                  }
              }
          }
          /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
          /// `start` and `end` are byte offsets.
          function slice(string memory subject, uint256 start, uint256 end)
              internal
              pure
              returns (string memory result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  let subjectLength := mload(subject)
                  if iszero(gt(subjectLength, end)) { end := subjectLength }
                  if iszero(gt(subjectLength, start)) { start := subjectLength }
                  if lt(start, end) {
                      result := mload(0x40)
                      let resultLength := sub(end, start)
                      mstore(result, resultLength)
                      subject := add(subject, start)
                      let w := not(0x1f)
                      // Copy the `subject` one word at a time, backwards.
                      for { let o := and(add(resultLength, 0x1f), w) } 1 {} {
                          mstore(add(result, o), mload(add(subject, o)))
                          o := add(o, w) // `sub(o, 0x20)`.
                          if iszero(o) { break }
                      }
                      // Zeroize the slot after the string.
                      mstore(add(add(result, 0x20), resultLength), 0)
                      // Allocate memory for the length and the bytes,
                      // rounded up to a multiple of 32.
                      mstore(0x40, add(result, and(add(resultLength, 0x3f), w)))
                  }
              }
          }
          /// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
          /// `start` is a byte offset.
          function slice(string memory subject, uint256 start)
              internal
              pure
              returns (string memory result)
          {
              result = slice(subject, start, uint256(int256(-1)));
          }
          /// @dev Returns all the indices of `search` in `subject`.
          /// The indices are byte offsets.
          function indicesOf(string memory subject, string memory search)
              internal
              pure
              returns (uint256[] memory result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  let subjectLength := mload(subject)
                  let searchLength := mload(search)
                  if iszero(gt(searchLength, subjectLength)) {
                      subject := add(subject, 0x20)
                      search := add(search, 0x20)
                      result := add(mload(0x40), 0x20)
                      let subjectStart := subject
                      let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)
                      let h := 0
                      if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
                      let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
                      let s := mload(search)
                      for {} 1 {} {
                          let t := mload(subject)
                          // Whether the first `searchLength % 32` bytes of
                          // `subject` and `search` matches.
                          if iszero(shr(m, xor(t, s))) {
                              if h {
                                  if iszero(eq(keccak256(subject, searchLength), h)) {
                                      subject := add(subject, 1)
                                      if iszero(lt(subject, subjectSearchEnd)) { break }
                                      continue
                                  }
                              }
                              // Append to `result`.
                              mstore(result, sub(subject, subjectStart))
                              result := add(result, 0x20)
                              // Advance `subject` by `searchLength`.
                              subject := add(subject, searchLength)
                              if searchLength {
                                  if iszero(lt(subject, subjectSearchEnd)) { break }
                                  continue
                              }
                          }
                          subject := add(subject, 1)
                          if iszero(lt(subject, subjectSearchEnd)) { break }
                      }
                      let resultEnd := result
                      // Assign `result` to the free memory pointer.
                      result := mload(0x40)
                      // Store the length of `result`.
                      mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))
                      // Allocate memory for result.
                      // We allocate one more word, so this array can be recycled for {split}.
                      mstore(0x40, add(resultEnd, 0x20))
                  }
              }
          }
          /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.
          function split(string memory subject, string memory delimiter)
              internal
              pure
              returns (string[] memory result)
          {
              uint256[] memory indices = indicesOf(subject, delimiter);
              /// @solidity memory-safe-assembly
              assembly {
                  let w := not(0x1f)
                  let indexPtr := add(indices, 0x20)
                  let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
                  mstore(add(indicesEnd, w), mload(subject))
                  mstore(indices, add(mload(indices), 1))
                  let prevIndex := 0
                  for {} 1 {} {
                      let index := mload(indexPtr)
                      mstore(indexPtr, 0x60)
                      if iszero(eq(index, prevIndex)) {
                          let element := mload(0x40)
                          let elementLength := sub(index, prevIndex)
                          mstore(element, elementLength)
                          // Copy the `subject` one word at a time, backwards.
                          for { let o := and(add(elementLength, 0x1f), w) } 1 {} {
                              mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
                              o := add(o, w) // `sub(o, 0x20)`.
                              if iszero(o) { break }
                          }
                          // Zeroize the slot after the string.
                          mstore(add(add(element, 0x20), elementLength), 0)
                          // Allocate memory for the length and the bytes,
                          // rounded up to a multiple of 32.
                          mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))
                          // Store the `element` into the array.
                          mstore(indexPtr, element)
                      }
                      prevIndex := add(index, mload(delimiter))
                      indexPtr := add(indexPtr, 0x20)
                      if iszero(lt(indexPtr, indicesEnd)) { break }
                  }
                  result := indices
                  if iszero(mload(delimiter)) {
                      result := add(indices, 0x20)
                      mstore(result, sub(mload(indices), 2))
                  }
              }
          }
          /// @dev Returns a concatenated string of `a` and `b`.
          /// Cheaper than `string.concat()` and does not de-align the free memory pointer.
          function concat(string memory a, string memory b)
              internal
              pure
              returns (string memory result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  let w := not(0x1f)
                  result := mload(0x40)
                  let aLength := mload(a)
                  // Copy `a` one word at a time, backwards.
                  for { let o := and(add(aLength, 0x20), w) } 1 {} {
                      mstore(add(result, o), mload(add(a, o)))
                      o := add(o, w) // `sub(o, 0x20)`.
                      if iszero(o) { break }
                  }
                  let bLength := mload(b)
                  let output := add(result, aLength)
                  // Copy `b` one word at a time, backwards.
                  for { let o := and(add(bLength, 0x20), w) } 1 {} {
                      mstore(add(output, o), mload(add(b, o)))
                      o := add(o, w) // `sub(o, 0x20)`.
                      if iszero(o) { break }
                  }
                  let totalLength := add(aLength, bLength)
                  let last := add(add(result, 0x20), totalLength)
                  // Zeroize the slot after the string.
                  mstore(last, 0)
                  // Stores the length.
                  mstore(result, totalLength)
                  // Allocate memory for the length and the bytes,
                  // rounded up to a multiple of 32.
                  mstore(0x40, and(add(last, 0x1f), w))
              }
          }
          /// @dev Returns a copy of the string in either lowercase or UPPERCASE.
          /// WARNING! This function is only compatible with 7-bit ASCII strings.
          function toCase(string memory subject, bool toUpper)
              internal
              pure
              returns (string memory result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  let length := mload(subject)
                  if length {
                      result := add(mload(0x40), 0x20)
                      subject := add(subject, 1)
                      let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
                      let w := not(0)
                      for { let o := length } 1 {} {
                          o := add(o, w)
                          let b := and(0xff, mload(add(subject, o)))
                          mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))
                          if iszero(o) { break }
                      }
                      result := mload(0x40)
                      mstore(result, length) // Store the length.
                      let last := add(add(result, 0x20), length)
                      mstore(last, 0) // Zeroize the slot after the string.
                      mstore(0x40, add(last, 0x20)) // Allocate the memory.
                  }
              }
          }
          /// @dev Returns a string from a small bytes32 string.
          /// `s` must be null-terminated, or behavior will be undefined.
          function fromSmallString(bytes32 s) internal pure returns (string memory result) {
              /// @solidity memory-safe-assembly
              assembly {
                  result := mload(0x40)
                  let n := 0
                  for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\\0'.
                  mstore(result, n)
                  let o := add(result, 0x20)
                  mstore(o, s)
                  mstore(add(o, n), 0)
                  mstore(0x40, add(result, 0x40))
              }
          }
          /// @dev Returns the small string, with all bytes after the first null byte zeroized.
          function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {
              /// @solidity memory-safe-assembly
              assembly {
                  for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\\0'.
                  mstore(0x00, s)
                  mstore(result, 0x00)
                  result := mload(0x00)
              }
          }
          /// @dev Returns the string as a normalized null-terminated small string.
          function toSmallString(string memory s) internal pure returns (bytes32 result) {
              /// @solidity memory-safe-assembly
              assembly {
                  result := mload(s)
                  if iszero(lt(result, 33)) {
                      mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.
                      revert(0x1c, 0x04)
                  }
                  result := shl(shl(3, sub(32, result)), mload(add(s, result)))
              }
          }
          /// @dev Returns a lowercased copy of the string.
          /// WARNING! This function is only compatible with 7-bit ASCII strings.
          function lower(string memory subject) internal pure returns (string memory result) {
              result = toCase(subject, false);
          }
          /// @dev Returns an UPPERCASED copy of the string.
          /// WARNING! This function is only compatible with 7-bit ASCII strings.
          function upper(string memory subject) internal pure returns (string memory result) {
              result = toCase(subject, true);
          }
          /// @dev Escapes the string to be used within HTML tags.
          function escapeHTML(string memory s) internal pure returns (string memory result) {
              /// @solidity memory-safe-assembly
              assembly {
                  let end := add(s, mload(s))
                  result := add(mload(0x40), 0x20)
                  // Store the bytes of the packed offsets and strides into the scratch space.
                  // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.
                  mstore(0x1f, 0x900094)
                  mstore(0x08, 0xc0000000a6ab)
                  // Store "&quot;&amp;&#39;&lt;&gt;" into the scratch space.
                  mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
                  for {} iszero(eq(s, end)) {} {
                      s := add(s, 1)
                      let c := and(mload(s), 0xff)
                      // Not in `["\\"","'","&","<",">"]`.
                      if iszero(and(shl(c, 1), 0x500000c400000000)) {
                          mstore8(result, c)
                          result := add(result, 1)
                          continue
                      }
                      let t := shr(248, mload(c))
                      mstore(result, mload(and(t, 0x1f)))
                      result := add(result, shr(5, t))
                  }
                  let last := result
                  mstore(last, 0) // Zeroize the slot after the string.
                  result := mload(0x40)
                  mstore(result, sub(last, add(result, 0x20))) // Store the length.
                  mstore(0x40, add(last, 0x20)) // Allocate the memory.
              }
          }
          /// @dev Escapes the string to be used within double-quotes in a JSON.
          /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.
          function escapeJSON(string memory s, bool addDoubleQuotes)
              internal
              pure
              returns (string memory result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  let end := add(s, mload(s))
                  result := add(mload(0x40), 0x20)
                  if addDoubleQuotes {
                      mstore8(result, 34)
                      result := add(1, result)
                  }
                  // Store "\\\\u0000" in scratch space.
                  // Store "0123456789abcdef" in scratch space.
                  // Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`.
                  // into the scratch space.
                  mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
                  // Bitmask for detecting `["\\"","\\\\"]`.
                  let e := or(shl(0x22, 1), shl(0x5c, 1))
                  for {} iszero(eq(s, end)) {} {
                      s := add(s, 1)
                      let c := and(mload(s), 0xff)
                      if iszero(lt(c, 0x20)) {
                          if iszero(and(shl(c, 1), e)) {
                              // Not in `["\\"","\\\\"]`.
                              mstore8(result, c)
                              result := add(result, 1)
                              continue
                          }
                          mstore8(result, 0x5c) // "\\\\".
                          mstore8(add(result, 1), c)
                          result := add(result, 2)
                          continue
                      }
                      if iszero(and(shl(c, 1), 0x3700)) {
                          // Not in `["\\b","\\t","\
      ","\\f","\\d"]`.
                          mstore8(0x1d, mload(shr(4, c))) // Hex value.
                          mstore8(0x1e, mload(and(c, 15))) // Hex value.
                          mstore(result, mload(0x19)) // "\\\\u00XX".
                          result := add(result, 6)
                          continue
                      }
                      mstore8(result, 0x5c) // "\\\\".
                      mstore8(add(result, 1), mload(add(c, 8)))
                      result := add(result, 2)
                  }
                  if addDoubleQuotes {
                      mstore8(result, 34)
                      result := add(1, result)
                  }
                  let last := result
                  mstore(last, 0) // Zeroize the slot after the string.
                  result := mload(0x40)
                  mstore(result, sub(last, add(result, 0x20))) // Store the length.
                  mstore(0x40, add(last, 0x20)) // Allocate the memory.
              }
          }
          /// @dev Escapes the string to be used within double-quotes in a JSON.
          function escapeJSON(string memory s) internal pure returns (string memory result) {
              result = escapeJSON(s, false);
          }
          /// @dev Returns whether `a` equals `b`.
          function eq(string memory a, string memory b) internal pure returns (bool result) {
              /// @solidity memory-safe-assembly
              assembly {
                  result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
              }
          }
          /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.
          function eqs(string memory a, bytes32 b) internal pure returns (bool result) {
              /// @solidity memory-safe-assembly
              assembly {
                  // These should be evaluated on compile time, as far as possible.
                  let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.
                  let x := not(or(m, or(b, add(m, and(b, m)))))
                  let r := shl(7, iszero(iszero(shr(128, x))))
                  r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
                  r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
                  r := or(r, shl(4, lt(0xffff, shr(r, x))))
                  r := or(r, shl(3, lt(0xff, shr(r, x))))
                  // forgefmt: disable-next-item
                  result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),
                      xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))
              }
          }
          /// @dev Packs a single string with its length into a single word.
          /// Returns `bytes32(0)` if the length is zero or greater than 31.
          function packOne(string memory a) internal pure returns (bytes32 result) {
              /// @solidity memory-safe-assembly
              assembly {
                  // We don't need to zero right pad the string,
                  // since this is our own custom non-standard packing scheme.
                  result :=
                      mul(
                          // Load the length and the bytes.
                          mload(add(a, 0x1f)),
                          // `length != 0 && length < 32`. Abuses underflow.
                          // Assumes that the length is valid and within the block gas limit.
                          lt(sub(mload(a), 1), 0x1f)
                      )
              }
          }
          /// @dev Unpacks a string packed using {packOne}.
          /// Returns the empty string if `packed` is `bytes32(0)`.
          /// If `packed` is not an output of {packOne}, the output behavior is undefined.
          function unpackOne(bytes32 packed) internal pure returns (string memory result) {
              /// @solidity memory-safe-assembly
              assembly {
                  // Grab the free memory pointer.
                  result := mload(0x40)
                  // Allocate 2 words (1 for the length, 1 for the bytes).
                  mstore(0x40, add(result, 0x40))
                  // Zeroize the length slot.
                  mstore(result, 0)
                  // Store the length and bytes.
                  mstore(add(result, 0x1f), packed)
                  // Right pad with zeroes.
                  mstore(add(add(result, 0x20), mload(result)), 0)
              }
          }
          /// @dev Packs two strings with their lengths into a single word.
          /// Returns `bytes32(0)` if combined length is zero or greater than 30.
          function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
              /// @solidity memory-safe-assembly
              assembly {
                  let aLength := mload(a)
                  // We don't need to zero right pad the strings,
                  // since this is our own custom non-standard packing scheme.
                  result :=
                      mul(
                          // Load the length and the bytes of `a` and `b`.
                          or(
                              shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),
                              mload(sub(add(b, 0x1e), aLength))
                          ),
                          // `totalLength != 0 && totalLength < 31`. Abuses underflow.
                          // Assumes that the lengths are valid and within the block gas limit.
                          lt(sub(add(aLength, mload(b)), 1), 0x1e)
                      )
              }
          }
          /// @dev Unpacks strings packed using {packTwo}.
          /// Returns the empty strings if `packed` is `bytes32(0)`.
          /// If `packed` is not an output of {packTwo}, the output behavior is undefined.
          function unpackTwo(bytes32 packed)
              internal
              pure
              returns (string memory resultA, string memory resultB)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  // Grab the free memory pointer.
                  resultA := mload(0x40)
                  resultB := add(resultA, 0x40)
                  // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
                  mstore(0x40, add(resultB, 0x40))
                  // Zeroize the length slots.
                  mstore(resultA, 0)
                  mstore(resultB, 0)
                  // Store the lengths and bytes.
                  mstore(add(resultA, 0x1f), packed)
                  mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
                  // Right pad with zeroes.
                  mstore(add(add(resultA, 0x20), mload(resultA)), 0)
                  mstore(add(add(resultB, 0x20), mload(resultB)), 0)
              }
          }
          /// @dev Directly returns `a` without copying.
          function directReturn(string memory a) internal pure {
              assembly {
                  // Assumes that the string does not start from the scratch space.
                  let retStart := sub(a, 0x20)
                  let retSize := add(mload(a), 0x40)
                  // Right pad with zeroes. Just in case the string is produced
                  // by a method that doesn't zero right pad.
                  mstore(add(retStart, retSize), 0)
                  // Store the return offset.
                  mstore(retStart, 0x20)
                  // End the transaction, returning the string.
                  return(retStart, retSize)
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      /// @notice Library to encode strings in Base64.
      /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol)
      /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Base64.sol)
      /// @author Modified from (https://github.com/Brechtpd/base64/blob/main/base64.sol) by Brecht Devos - <brecht@loopring.org>.
      library Base64 {
          /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
          /// See: https://datatracker.ietf.org/doc/html/rfc4648
          /// @param fileSafe  Whether to replace '+' with '-' and '/' with '_'.
          /// @param noPadding Whether to strip away the padding.
          function encode(bytes memory data, bool fileSafe, bool noPadding)
              internal
              pure
              returns (string memory result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  let dataLength := mload(data)
                  if dataLength {
                      // Multiply by 4/3 rounded up.
                      // The `shl(2, ...)` is equivalent to multiplying by 4.
                      let encodedLength := shl(2, div(add(dataLength, 2), 3))
                      // Set `result` to point to the start of the free memory.
                      result := mload(0x40)
                      // Store the table into the scratch space.
                      // Offsetted by -1 byte so that the `mload` will load the character.
                      // We will rewrite the free memory pointer at `0x40` later with
                      // the allocated size.
                      // The magic constant 0x0670 will turn "-_" into "+/".
                      mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
                      mstore(0x3f, xor("ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0670)))
                      // Skip the first slot, which stores the length.
                      let ptr := add(result, 0x20)
                      let end := add(ptr, encodedLength)
                      // Run over the input, 3 bytes at a time.
                      for {} 1 {} {
                          data := add(data, 3) // Advance 3 bytes.
                          let input := mload(data)
                          // Write 4 bytes. Optimized for fewer stack operations.
                          mstore8(0, mload(and(shr(18, input), 0x3F)))
                          mstore8(1, mload(and(shr(12, input), 0x3F)))
                          mstore8(2, mload(and(shr(6, input), 0x3F)))
                          mstore8(3, mload(and(input, 0x3F)))
                          mstore(ptr, mload(0x00))
                          ptr := add(ptr, 4) // Advance 4 bytes.
                          if iszero(lt(ptr, end)) { break }
                      }
                      mstore(0x40, add(end, 0x20)) // Allocate the memory.
                      // Equivalent to `o = [0, 2, 1][dataLength % 3]`.
                      let o := div(2, mod(dataLength, 3))
                      // Offset `ptr` and pad with '='. We can simply write over the end.
                      mstore(sub(ptr, o), shl(240, 0x3d3d))
                      // Set `o` to zero if there is padding.
                      o := mul(iszero(iszero(noPadding)), o)
                      mstore(sub(ptr, o), 0) // Zeroize the slot after the string.
                      mstore(result, sub(encodedLength, o)) // Store the length.
                  }
              }
          }
          /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
          /// Equivalent to `encode(data, false, false)`.
          function encode(bytes memory data) internal pure returns (string memory result) {
              result = encode(data, false, false);
          }
          /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
          /// Equivalent to `encode(data, fileSafe, false)`.
          function encode(bytes memory data, bool fileSafe)
              internal
              pure
              returns (string memory result)
          {
              result = encode(data, fileSafe, false);
          }
          /// @dev Decodes base64 encoded `data`.
          ///
          /// Supports:
          /// - RFC 4648 (both standard and file-safe mode).
          /// - RFC 3501 (63: ',').
          ///
          /// Does not support:
          /// - Line breaks.
          ///
          /// Note: For performance reasons,
          /// this function will NOT revert on invalid `data` inputs.
          /// Outputs for invalid inputs will simply be undefined behaviour.
          /// It is the user's responsibility to ensure that the `data`
          /// is a valid base64 encoded string.
          function decode(string memory data) internal pure returns (bytes memory result) {
              /// @solidity memory-safe-assembly
              assembly {
                  let dataLength := mload(data)
                  if dataLength {
                      let decodedLength := mul(shr(2, dataLength), 3)
                      for {} 1 {} {
                          // If padded.
                          if iszero(and(dataLength, 3)) {
                              let t := xor(mload(add(data, dataLength)), 0x3d3d)
                              // forgefmt: disable-next-item
                              decodedLength := sub(
                                  decodedLength,
                                  add(iszero(byte(30, t)), iszero(byte(31, t)))
                              )
                              break
                          }
                          // If non-padded.
                          decodedLength := add(decodedLength, sub(and(dataLength, 3), 1))
                          break
                      }
                      result := mload(0x40)
                      // Write the length of the bytes.
                      mstore(result, decodedLength)
                      // Skip the first slot, which stores the length.
                      let ptr := add(result, 0x20)
                      let end := add(ptr, decodedLength)
                      // Load the table into the scratch space.
                      // Constants are optimized for smaller bytecode with zero gas overhead.
                      // `m` also doubles as the mask of the upper 6 bits.
                      let m := 0xfc000000fc00686c7074787c8084888c9094989ca0a4a8acb0b4b8bcc0c4c8cc
                      mstore(0x5b, m)
                      mstore(0x3b, 0x04080c1014181c2024282c3034383c4044484c5054585c6064)
                      mstore(0x1a, 0xf8fcf800fcd0d4d8dce0e4e8ecf0f4)
                      for {} 1 {} {
                          // Read 4 bytes.
                          data := add(data, 4)
                          let input := mload(data)
                          // Write 3 bytes.
                          // forgefmt: disable-next-item
                          mstore(ptr, or(
                              and(m, mload(byte(28, input))),
                              shr(6, or(
                                  and(m, mload(byte(29, input))),
                                  shr(6, or(
                                      and(m, mload(byte(30, input))),
                                      shr(6, mload(byte(31, input)))
                                  ))
                              ))
                          ))
                          ptr := add(ptr, 3)
                          if iszero(lt(ptr, end)) { break }
                      }
                      mstore(0x40, add(end, 0x20)) // Allocate the memory.
                      mstore(end, 0) // Zeroize the slot after the bytes.
                      mstore(0x60, 0) // Restore the zero slot.
                  }
              }
          }
      }
      // 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)
      ///
      /// @dev 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;
          uint256 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 = uint256(uint160(address(this)));
              _cachedChainId = block.chainid;
              string memory name;
              string memory version;
              if (!_domainNameAndVersionMayChange()) (name, version) = _domainNameAndVersion();
              bytes32 nameHash = _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(name));
              bytes32 versionHash =
                  _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(version));
              _cachedNameHash = nameHash;
              _cachedVersionHash = versionHash;
              bytes32 separator;
              if (!_domainNameAndVersionMayChange()) {
                  /// @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";
          ///     }
          /// ```
          ///
          /// Note: If the returned result may change after the contract has been deployed,
          /// you must override `_domainNameAndVersionMayChange()` to return true.
          function _domainNameAndVersion()
              internal
              view
              virtual
              returns (string memory name, string memory version);
          /// @dev Returns if `_domainNameAndVersion()` may change
          /// after the contract has been deployed (i.e. after the constructor).
          /// Default: false.
          function _domainNameAndVersionMayChange() internal pure virtual returns (bool result) {}
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                     HASHING OPERATIONS                     */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Returns the EIP-712 domain separator.
          function _domainSeparator() internal view virtual returns (bytes32 separator) {
              if (_domainNameAndVersionMayChange()) {
                  separator = _buildDomainSeparator();
              } else {
                  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) {
              // We will use `digest` to store the domain separator to save a bit of gas.
              if (_domainNameAndVersionMayChange()) {
                  digest = _buildDomainSeparator();
              } else {
                  digest = _cachedDomainSeparator;
                  if (_cachedDomainSeparatorInvalidated()) digest = _buildDomainSeparator();
              }
              /// @solidity memory-safe-assembly
              assembly {
                  // Compute the digest.
                  mstore(0x00, 0x1901000000000000) // Store "\\x19\\x01".
                  mstore(0x1a, digest) // 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) {
              // We will use `separator` to store the name hash to save a bit of gas.
              bytes32 versionHash;
              if (_domainNameAndVersionMayChange()) {
                  (string memory name, string memory version) = _domainNameAndVersion();
                  separator = keccak256(bytes(name));
                  versionHash = keccak256(bytes(version));
              } else {
                  separator = _cachedNameHash;
                  versionHash = _cachedVersionHash;
              }
              /// @solidity memory-safe-assembly
              assembly {
                  let m := mload(0x40) // Load the free memory pointer.
                  mstore(m, _DOMAIN_TYPEHASH)
                  mstore(add(m, 0x20), separator) // Name hash.
                  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;
              uint256 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 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)
      ///
      /// @dev Note:
      /// - The recovery functions use the ecrecover precompile (0x1).
      /// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure.
      ///   This is for more safety by default.
      ///   Use the `tryRecover` variants if you need to get the zero address back
      ///   upon recovery failure instead.
      /// - As of Solady version 0.0.134, all `bytes signature` variants accept both
      ///   regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
      ///   See: https://eips.ethereum.org/EIPS/eip-2098
      ///   This is for calldata efficiency on smart accounts prevalent on L2s.
      ///
      /// WARNING! Do NOT use signatures as unique identifiers:
      /// - Use a nonce in the digest to prevent replay attacks on the same contract.
      /// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
      ///   EIP-712 also enables readable signing of typed data for better user safety.
      /// This implementation does NOT check if a signature is non-malleable.
      library ECDSA {
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                        CUSTOM ERRORS                       */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The signature is invalid.
          error InvalidSignature();
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                    RECOVERY OPERATIONS                     */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
          function recover(bytes32 hash, bytes memory signature) internal view returns (address result) {
              /// @solidity memory-safe-assembly
              assembly {
                  result := 1
                  let m := mload(0x40) // Cache the free memory pointer.
                  for {} 1 {} {
                      mstore(0x00, hash)
                      mstore(0x40, mload(add(signature, 0x20))) // `r`.
                      if eq(mload(signature), 64) {
                          let vs := mload(add(signature, 0x40))
                          mstore(0x20, add(shr(255, vs), 27)) // `v`.
                          mstore(0x60, shr(1, shl(1, vs))) // `s`.
                          break
                      }
                      if eq(mload(signature), 65) {
                          mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                          mstore(0x60, mload(add(signature, 0x40))) // `s`.
                          break
                      }
                      result := 0
                      break
                  }
                  result :=
                      mload(
                          staticcall(
                              gas(), // Amount of gas left for the transaction.
                              result, // Address of `ecrecover`.
                              0x00, // Start of input.
                              0x80, // Size of input.
                              0x01, // Start of output.
                              0x20 // Size of output.
                          )
                      )
                  // `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`.
          function recoverCalldata(bytes32 hash, bytes calldata signature)
              internal
              view
              returns (address result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  result := 1
                  let m := mload(0x40) // Cache the free memory pointer.
                  mstore(0x00, hash)
                  for {} 1 {} {
                      if eq(signature.length, 64) {
                          let vs := calldataload(add(signature.offset, 0x20))
                          mstore(0x20, add(shr(255, vs), 27)) // `v`.
                          mstore(0x40, calldataload(signature.offset)) // `r`.
                          mstore(0x60, shr(1, shl(1, vs))) // `s`.
                          break
                      }
                      if eq(signature.length, 65) {
                          mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                          calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                          break
                      }
                      result := 0
                      break
                  }
                  result :=
                      mload(
                          staticcall(
                              gas(), // Amount of gas left for the transaction.
                              result, // Address of `ecrecover`.
                              0x00, // Start of input.
                              0x80, // Size of input.
                              0x01, // Start of output.
                              0x20 // Size of output.
                          )
                      )
                  // `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`.
          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`.
                  result :=
                      mload(
                          staticcall(
                              gas(), // Amount of gas left for the transaction.
                              1, // Address of `ecrecover`.
                              0x00, // Start of input.
                              0x80, // Size of input.
                              0x01, // Start of output.
                              0x20 // Size of output.
                          )
                      )
                  // `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)
                  result :=
                      mload(
                          staticcall(
                              gas(), // Amount of gas left for the transaction.
                              1, // Address of `ecrecover`.
                              0x00, // Start of input.
                              0x80, // Size of input.
                              0x01, // Start of output.
                              0x20 // Size of output.
                          )
                      )
                  // `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`.
          function tryRecover(bytes32 hash, bytes memory signature)
              internal
              view
              returns (address result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  result := 1
                  let m := mload(0x40) // Cache the free memory pointer.
                  for {} 1 {} {
                      mstore(0x00, hash)
                      mstore(0x40, mload(add(signature, 0x20))) // `r`.
                      if eq(mload(signature), 64) {
                          let vs := mload(add(signature, 0x40))
                          mstore(0x20, add(shr(255, vs), 27)) // `v`.
                          mstore(0x60, shr(1, shl(1, vs))) // `s`.
                          break
                      }
                      if eq(mload(signature), 65) {
                          mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                          mstore(0x60, mload(add(signature, 0x40))) // `s`.
                          break
                      }
                      result := 0
                      break
                  }
                  pop(
                      staticcall(
                          gas(), // Amount of gas left for the transaction.
                          result, // 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`.
          function tryRecoverCalldata(bytes32 hash, bytes calldata signature)
              internal
              view
              returns (address result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  result := 1
                  let m := mload(0x40) // Cache the free memory pointer.
                  mstore(0x00, hash)
                  for {} 1 {} {
                      if eq(signature.length, 64) {
                          let vs := calldataload(add(signature.offset, 0x20))
                          mstore(0x20, add(shr(255, vs), 27)) // `v`.
                          mstore(0x40, calldataload(signature.offset)) // `r`.
                          mstore(0x60, shr(1, shl(1, vs))) // `s`.
                          break
                      }
                      if eq(signature.length, 65) {
                          mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                          calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                          break
                      }
                      result := 0
                      break
                  }
                  pop(
                      staticcall(
                          gas(), // Amount of gas left for the transaction.
                          result, // 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`.
          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.
                          1, // 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.
                          1, // 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 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 DoS 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.
          uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                       ETH OPERATIONS                       */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
          //
          // The regular variants:
          // - Forwards all remaining gas to the target.
          // - Reverts if the target reverts.
          // - Reverts if the current contract has insufficient balance.
          //
          // The force variants:
          // - Forwards with an optional gas stipend
          //   (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
          // - If the target reverts, or if the gas stipend is exhausted,
          //   creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
          //   Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
          // - Reverts if the current contract has insufficient balance.
          //
          // The try variants:
          // - Forwards with a mandatory gas stipend.
          // - Instead of reverting, returns whether the transfer succeeded.
          /// @dev Sends `amount` (in wei) ETH to `to`.
          function safeTransferETH(address to, uint256 amount) internal {
              /// @solidity memory-safe-assembly
              assembly {
                  if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
                      mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                      revert(0x1c, 0x04)
                  }
              }
          }
          /// @dev Sends all the ETH in the current contract to `to`.
          function safeTransferAllETH(address to) internal {
              /// @solidity memory-safe-assembly
              assembly {
                  // Transfer all the ETH and check if it succeeded or not.
                  if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                      mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                      revert(0x1c, 0x04)
                  }
              }
          }
          /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
          function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
              /// @solidity memory-safe-assembly
              assembly {
                  if lt(selfbalance(), amount) {
                      mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                      revert(0x1c, 0x04)
                  }
                  if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                      mstore(0x00, to) // Store the address in scratch space.
                      mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                      mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                      if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
                  }
              }
          }
          /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
          function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
              /// @solidity memory-safe-assembly
              assembly {
                  if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                      mstore(0x00, to) // Store the address in scratch space.
                      mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                      mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                      if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
                  }
              }
          }
          /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
          function forceSafeTransferETH(address to, uint256 amount) internal {
              /// @solidity memory-safe-assembly
              assembly {
                  if lt(selfbalance(), amount) {
                      mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                      revert(0x1c, 0x04)
                  }
                  if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                      mstore(0x00, to) // Store the address in scratch space.
                      mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                      mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                      if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
                  }
              }
          }
          /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
          function forceSafeTransferAllETH(address to) internal {
              /// @solidity memory-safe-assembly
              assembly {
                  // forgefmt: disable-next-item
                  if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                      mstore(0x00, to) // Store the address in scratch space.
                      mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                      mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                      if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
                  }
              }
          }
          /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
          function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
              internal
              returns (bool success)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
              }
          }
          /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
          function trySafeTransferAllETH(address to, uint256 gasStipend)
              internal
              returns (bool success)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                      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.
                  mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
                  // Perform the transfer, reverting upon failure.
                  if iszero(
                      and( // The arguments of `and` are evaluated from right to left.
                          or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                          call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                      )
                  ) {
                      mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                      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.
                  mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
                  // Read the balance, reverting upon failure.
                  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)
                      )
                  ) {
                      mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                      revert(0x1c, 0x04)
                  }
                  mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
                  amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
                  // Perform the transfer, reverting upon failure.
                  if iszero(
                      and( // The arguments of `and` are evaluated from right to left.
                          or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                          call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                      )
                  ) {
                      mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                      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.
                  mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
                  // Perform the transfer, reverting upon failure.
                  if iszero(
                      and( // The arguments of `and` are evaluated from right to left.
                          or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                          call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                      )
                  ) {
                      mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                      revert(0x1c, 0x04)
                  }
                  mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
              }
          }
          /// @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.
                  // Read the balance, reverting upon failure.
                  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)
                      )
                  ) {
                      mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                      revert(0x1c, 0x04)
                  }
                  mstore(0x14, to) // Store the `to` argument.
                  amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
                  mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
                  // Perform the transfer, reverting upon failure.
                  if iszero(
                      and( // The arguments of `and` are evaluated from right to left.
                          or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                          call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                      )
                  ) {
                      mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                      revert(0x1c, 0x04)
                  }
                  mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
              }
          }
          /// @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.
                  mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                  // Perform the approval, reverting upon failure.
                  if iszero(
                      and( // The arguments of `and` are evaluated from right to left.
                          or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                          call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                      )
                  ) {
                      mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                      revert(0x1c, 0x04)
                  }
                  mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
              }
          }
          /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
          /// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
          /// then retries the approval again (some tokens, e.g. USDT, requires this).
          /// Reverts upon failure.
          function safeApproveWithRetry(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.
                  mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                  // Perform the approval, retrying upon failure.
                  if iszero(
                      and( // The arguments of `and` are evaluated from right to left.
                          or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                          call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                      )
                  ) {
                      mstore(0x34, 0) // Store 0 for the `amount`.
                      mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                      pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
                      mstore(0x34, amount) // Store back the original `amount`.
                      // Retry the approval, reverting upon failure.
                      if iszero(
                          and(
                              or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                              call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                          )
                      ) {
                          mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                          revert(0x1c, 0x04)
                      }
                  }
                  mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
              }
          }
          /// @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.
                  mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
                  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)
                          )
                      )
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.18;
      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.18;
      interface IReentrancyGuard {
          error ReentrancyGuard__ReentrantCall();
      }