ETH Price: $2,566.59 (-4.01%)

Transaction Decoder

Block:
22082988 at Mar-19-2025 07:26:11 PM +UTC
Transaction Fee:
0.000777561764007488 ETH $2.00
Gas Used:
397,718 Gas / 1.955058016 Gwei

Emitted Events:

11 WalletSimple.Deposited( from=Forwarder, value=47230370000000000, data=0x )
12 Forwarder.ForwarderDeposited( from=[Receiver] 0xa9d1e08c7793af67e9d92fe308d5697fb81d3e43, value=47230370000000000, data=0x )

Account State Difference:

  Address   Before After State Difference Code
0x0c30D248...0849D834A 0 Eth0.01142889 Eth0.01142889
0x19250a9E...C4d3d397f
0 Eth
Nonce: 0
0.000114395 Eth
Nonce: 0
0.000114395From: 0 To: 0
0x1CaB5Cd3...3d3F466e7 0.00004299447375976 Eth0.000114395 Eth0.00007140052624024
0x2e4C7323...41Dfa3cCe 0.04820188 Eth0.06461268 Eth0.0164108
0x30B9BDcA...d74fD9fe5 0.00000528468129 Eth0.00209076468129 Eth0.00208548
0x340E0D87...cc3847F45 0.00024724181066898 Eth0.01964025181066898 Eth0.01939301
0x45919227...5d0abd56C 0.00491416103616216 Eth0.00711212103616216 Eth0.00219796
0x4bD41493...c0a3EBa7b 0.044285385765151 Eth0.108318165765151 Eth0.06403278
0x4E5f7bFF...2C98C95A2 0.000554577054311287 Eth0.016019927054311287 Eth0.01546535
0x4Ffd1989...Cbea23697 0.000049464194034412 Eth0.000114425 Eth0.000064960805965588
0x5AA971a6...5BF2616A6 0.001826889247424592 Eth0.026194089247424592 Eth0.0243672
0x6016AEcd...208151cf7 0.03484229 Eth0.04382511 Eth0.00898282
0x691dC089...bEA508C2c 0 Eth0.01994077 Eth0.01994077
0x778574B2...ecb41ecf6 0.000002029649013 Eth0.121575399649013 Eth0.12157337
0x7830c87C...31FA86F43
(Coinbase: Deposit)
106.341384413363355246 Eth
Nonce: 2229431
106.340606851599347758 Eth
Nonce: 2229432
0.000777561764007488
0x7edc1B9B...bbC20c06E 0.000010232882688 Eth0.013627362882688 Eth0.01361713
0x94bEB90F...83B675B58 19.718115516706141 Eth20.337940656706141 Eth0.61982514
(beaverbuild)
5.934745472571035044 Eth5.935143190571035044 Eth0.000397718
0xA9D1e08C...FB81d3E43
(Coinbase 10)
2,783.587640142827698005 Eth2,781.340113866495492177 Eth2.247526276332205828
0xaca62292...93613F171 0.2312557 Eth0.46463297 Eth0.23337727
0xAe4aaBd4...0d1F0aE67 0.0175304724260305 Eth0.1407211124260305 Eth0.12319064
0xaFB9D14F...D3D12Deb4 27.395151004133630055 Eth27.442381374133630055 Eth0.04723037
0xB505EbD2...C21632a87 0.000309001842334943 Eth0.000924701842334943 Eth0.0006157
0xB9fBd733...35d7BD930 0.783635864283232 Eth1.190508514283232 Eth0.40687265
0xBADEa656...8ff4c24c1 0.07826777 Eth0.08481891 Eth0.00655114
0xCa255711...93F56Dbd7 0.06607774 Eth0.09541749 Eth0.02933975
0xCbf282b6...77E3a9BAC 0 Eth0.04667341 Eth0.04667341
0xCfeC1e1E...C0D1500a1 0.000184673028555256 Eth0.032049033028555256 Eth0.03186436
0xd318c235...d89813a9c 0.019216593630866213 Eth0.214650943630866213 Eth0.19543435
0xD4711f44...E791443e1 0.000104775440952 Eth0.059112585440952 Eth0.05900781
0xdfBFD6A4...177c7b2cC 0.000232430153554144 Eth0.006041990153554144 Eth0.00580956
0xeDbaF77b...E4ed777d6 0.000000371825328 Eth0.027721921825328 Eth0.02772155
0xf96a1b0f...afb9a52Bc 0.001075851582076435 Eth0.047987171582076435 Eth0.04691132
0xFF369268...BF223E7D6 0.28842526 Eth0.3357802 Eth0.04735494

Execution Trace

Coinbase 10.1a1da075( )
  • ETH 0.04723037 Forwarder.CALL( )
    • ETH 0.04723037 Forwarder.DELEGATECALL( )
      • ETH 0.04723037 WalletSimple.CALL( )
        • ETH 0.04723037 WalletSimple.DELEGATECALL( )
        • ETH 0.02772155 0xedbaf77be1c2c2a2617b0eca765c2fbe4ed777d6.CALL( )
        • ETH 0.19543435 0xd318c2357c268e6f362c3ca48969125d89813a9c.CALL( )
        • ETH 0.01939301 0x340e0d87336d0a3da459ee220bce53dcc3847f45.CALL( )
        • ETH 0.05900781 0xd4711f44f1efc4c17c7c7d651118ae8e791443e1.CALL( )
        • ETH 0.03186436 0xcfec1e1eef68b6d5d641b6171fd6296c0d1500a1.CALL( )
        • ETH 0.00655114 0xbadea65621710308bf7517623be90748ff4c24c1.CALL( )
        • ETH 0.12319064 0xae4aabd4d6f66adcd1f52f781c027e10d1f0ae67.CALL( )
        • ETH 0.06403278 0x4bd414936919fa6b0d7102bc56d172dc0a3eba7b.CALL( )
        • ETH 0.01361713 0x7edc1b9b9852d9112642cdbcfc2fefcbbc20c06e.CALL( )
        • ETH 0.01546535 0x4e5f7bff0550328521d747acd67b4672c98c95a2.CALL( )
        • ETH 0.0164108 0x2e4c7323f92b3703a2a571ef9dd2d4041dfa3cce.CALL( )
        • ETH 0.0006157 0xb505ebd22e4b9b4914277c6e74ac0e3c21632a87.CALL( )
        • ETH 0.00898282 0x6016aecdd2f50248af3b5f600a8841a208151cf7.CALL( )
        • ETH 0.01994077 0x691dc08930704828dc73a18ddc18834bea508c2c.CALL( )
        • ETH 0.00007140052624024 0x1cab5cd3db4940a3ebbca47d145da4f3d3f466e7.CALL( )
        • ETH 0.000064960805965588 0x4ffd1989d607d07815eb1971cfb5b09cbea23697.CALL( )
        • ETH 0.000114395 0x19250a9ed4a7d3be4be9c2695fc9f34c4d3d397f.CALL( )
        • ETH 0.40687265 0xb9fbd733026cae9ade18b8f981d36d335d7bd930.CALL( )
        • ETH 0.04667341 0xcbf282b6c85defa2e4a51b7cea710a877e3a9bac.CALL( )
        • ETH 0.23337727 0xaca622922b9cbe1898052e1e9bc075f93613f171.CALL( )
        • ETH 0.01142889 0x0c30d2489a22efd35b9ee8771df47950849d834a.CALL( )
        • ETH 0.04691132 0xf96a1b0f7238e5ff8eb7dcd55c27da9afb9a52bc.CALL( )
        • ETH 0.61982514 0x94beb90fb8d2bcbab0be280f86356a183b675b58.CALL( )
        • ETH 0.00208548 0x30b9bdca32fa1c0d878204bac26722cd74fd9fe5.CALL( )
        • ETH 0.0243672 CoinbaseSmartWallet.CALL( )
          • ETH 0.0243672 CoinbaseSmartWallet.DELEGATECALL( )
          • ETH 0.00219796 0x459192271366c77ef3abb64673d14d85d0abd56c.CALL( )
          • ETH 0.00580956 0xdfbfd6a4ea6893bb0a2a35eb1e77344177c7b2cc.CALL( )
          • ETH 0.04735494 0xff3692682c4e3c62c94240fa2fd02bbbf223e7d6.CALL( )
          • ETH 0.12157337 0x778574b2e491c2ceffeca47518f45a7ecb41ecf6.CALL( )
          • ETH 0.02933975 0xca2557112a8da707c71d7cdba16d25893f56dbd7.CALL( )
            File 1 of 6: WalletSimple
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity 0.8.10;
            import './TransferHelper.sol';
            import './ERC20Interface.sol';
            import './IForwarder.sol';
            /** ERC721, ERC1155 imports */
            import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol';
            import '@openzeppelin/contracts/token/ERC1155/utils/ERC1155Receiver.sol';
            /**
             *
             * WalletSimple
             * ============
             *
             * Basic multi-signer wallet designed for use in a co-signing environment where 2 signatures are required to move funds.
             * Typically used in a 2-of-3 signing configuration. Uses ecrecover to allow for 2 signatures in a single transaction.
             *
             * The first signature is created on the operation hash (see Data Formats) and passed to sendMultiSig/sendMultiSigToken
             * The signer is determined by verifyMultiSig().
             *
             * The second signature is created by the submitter of the transaction and determined by msg.signer.
             *
             * Data Formats
             * ============
             *
             * The signature is created with ethereumjs-util.ecsign(operationHash).
             * Like the eth_sign RPC call, it packs the values as a 65-byte array of [r, s, v].
             * Unlike eth_sign, the message is not prefixed.
             *
             * The operationHash the result of keccak256(prefix, toAddress, value, data, expireTime).
             * For ether transactions, `prefix` is "ETHER".
             * For token transaction, `prefix` is "ERC20" and `data` is the tokenContractAddress.
             *
             *
             */
            contract WalletSimple is IERC721Receiver, ERC1155Receiver {
              // Events
              event Deposited(address from, uint256 value, bytes data);
              event SafeModeActivated(address msgSender);
              event Transacted(
                address msgSender, // Address of the sender of the message initiating the transaction
                address otherSigner, // Address of the signer (second signature) used to initiate the transaction
                bytes32 operation, // Operation hash (see Data Formats)
                address toAddress, // The address the transaction was sent to
                uint256 value, // Amount of Wei sent to the address
                bytes data // Data sent when invoking the transaction
              );
              event BatchTransfer(address sender, address recipient, uint256 value);
              // this event shows the other signer and the operation hash that they signed
              // specific batch transfer events are emitted in Batcher
              event BatchTransacted(
                address msgSender, // Address of the sender of the message initiating the transaction
                address otherSigner, // Address of the signer (second signature) used to initiate the transaction
                bytes32 operation // Operation hash (see Data Formats)
              );
              // Public fields
              mapping(address => bool) public signers; // The addresses that can co-sign transactions on the wallet
              bool public safeMode = false; // When active, wallet may only send to signer addresses
              bool public initialized = false; // True if the contract has been initialized
              // Internal fields
              uint256 private constant MAX_SEQUENCE_ID_INCREASE = 10000;
              uint256 constant SEQUENCE_ID_WINDOW_SIZE = 10;
              uint256[SEQUENCE_ID_WINDOW_SIZE] recentSequenceIds;
              /**
               * Set up a simple multi-sig wallet by specifying the signers allowed to be used on this wallet.
               * 2 signers will be required to send a transaction from this wallet.
               * Note: The sender is NOT automatically added to the list of signers.
               * Signers CANNOT be changed once they are set
               *
               * @param allowedSigners An array of signers on the wallet
               */
              function init(address[] calldata allowedSigners) external onlyUninitialized {
                require(allowedSigners.length == 3, 'Invalid number of signers');
                for (uint8 i = 0; i < allowedSigners.length; i++) {
                  require(allowedSigners[i] != address(0), 'Invalid signer');
                  signers[allowedSigners[i]] = true;
                }
                initialized = true;
              }
              /**
               * Get the network identifier that signers must sign over
               * This provides protection signatures being replayed on other chains
               * This must be a virtual function because chain-specific contracts will need
               *    to override with their own network ids. It also can't be a field
               *    to allow this contract to be used by proxy with delegatecall, which will
               *    not pick up on state variables
               */
              function getNetworkId() internal virtual pure returns (string memory) {
                return 'ETHER';
              }
              /**
               * Get the network identifier that signers must sign over for token transfers
               * This provides protection signatures being replayed on other chains
               * This must be a virtual function because chain-specific contracts will need
               *    to override with their own network ids. It also can't be a field
               *    to allow this contract to be used by proxy with delegatecall, which will
               *    not pick up on state variables
               */
              function getTokenNetworkId() internal virtual pure returns (string memory) {
                return 'ERC20';
              }
              /**
               * Get the network identifier that signers must sign over for batch transfers
               * This provides protection signatures being replayed on other chains
               * This must be a virtual function because chain-specific contracts will need
               *    to override with their own network ids. It also can't be a field
               *    to allow this contract to be used by proxy with delegatecall, which will
               *    not pick up on state variables
               */
              function getBatchNetworkId() internal virtual pure returns (string memory) {
                return 'ETHER-Batch';
              }
              /**
               * Determine if an address is a signer on this wallet
               * @param signer address to check
               * returns boolean indicating whether address is signer or not
               */
              function isSigner(address signer) public view returns (bool) {
                return signers[signer];
              }
              /**
               * Modifier that will execute internal code block only if the sender is an authorized signer on this wallet
               */
              modifier onlySigner {
                require(isSigner(msg.sender), 'Non-signer in onlySigner method');
                _;
              }
              /**
               * Modifier that will execute internal code block only if the contract has not been initialized yet
               */
              modifier onlyUninitialized {
                require(!initialized, 'Contract already initialized');
                _;
              }
              /**
               * Gets called when a transaction is received with data that does not match any other method
               */
              fallback() external payable {
                if (msg.value > 0) {
                  // Fire deposited event if we are receiving funds
                  emit Deposited(msg.sender, msg.value, msg.data);
                }
              }
              /**
               * Gets called when a transaction is received with ether and no data
               */
              receive() external payable {
                if (msg.value > 0) {
                  // Fire deposited event if we are receiving funds
                  // message data is always empty for receive. If there is data it is sent to fallback function.
                  emit Deposited(msg.sender, msg.value, '');
                }
              }
              /**
               * Execute a multi-signature transaction from this wallet using 2 signers: one from msg.sender and the other from ecrecover.
               * Sequence IDs are numbers starting from 1. They are used to prevent replay attacks and may not be repeated.
               *
               * @param toAddress the destination address to send an outgoing transaction
               * @param value the amount in Wei to be sent
               * @param data the data to send to the toAddress when invoking the transaction
               * @param expireTime the number of seconds since 1970 for which this transaction is valid
               * @param sequenceId the unique sequence id obtainable from getNextSequenceId
               * @param signature see Data Formats
               */
              function sendMultiSig(
                address toAddress,
                uint256 value,
                bytes calldata data,
                uint256 expireTime,
                uint256 sequenceId,
                bytes calldata signature
              ) external onlySigner {
                // Verify the other signer
                bytes32 operationHash = keccak256(
                  abi.encodePacked(
                    getNetworkId(),
                    toAddress,
                    value,
                    data,
                    expireTime,
                    sequenceId
                  )
                );
                address otherSigner = verifyMultiSig(
                  toAddress,
                  operationHash,
                  signature,
                  expireTime,
                  sequenceId
                );
                // Success, send the transaction
                (bool success, ) = toAddress.call{ value: value }(data);
                require(success, 'Call execution failed');
                emit Transacted(
                  msg.sender,
                  otherSigner,
                  operationHash,
                  toAddress,
                  value,
                  data
                );
              }
              /**
               * Execute a batched multi-signature transaction from this wallet using 2 signers: one from msg.sender and the other from ecrecover.
               * Sequence IDs are numbers starting from 1. They are used to prevent replay attacks and may not be repeated.
               * The recipients and values to send are encoded in two arrays, where for index i, recipients[i] will be sent values[i].
               *
               * @param recipients The list of recipients to send to
               * @param values The list of values to send to
               * @param expireTime the number of seconds since 1970 for which this transaction is valid
               * @param sequenceId the unique sequence id obtainable from getNextSequenceId
               * @param signature see Data Formats
               */
              function sendMultiSigBatch(
                address[] calldata recipients,
                uint256[] calldata values,
                uint256 expireTime,
                uint256 sequenceId,
                bytes calldata signature
              ) external onlySigner {
                require(recipients.length != 0, 'Not enough recipients');
                require(
                  recipients.length == values.length,
                  'Unequal recipients and values'
                );
                require(recipients.length < 256, 'Too many recipients, max 255');
                // Verify the other signer
                bytes32 operationHash = keccak256(
                  abi.encodePacked(
                    getBatchNetworkId(),
                    recipients,
                    values,
                    expireTime,
                    sequenceId
                  )
                );
                // the first parameter (toAddress) is used to ensure transactions in safe mode only go to a signer
                // if in safe mode, we should use normal sendMultiSig to recover, so this check will always fail if in safe mode
                require(!safeMode, 'Batch in safe mode');
                address otherSigner = verifyMultiSig(
                  address(0x0),
                  operationHash,
                  signature,
                  expireTime,
                  sequenceId
                );
                batchTransfer(recipients, values);
                emit BatchTransacted(msg.sender, otherSigner, operationHash);
              }
              /**
               * Transfer funds in a batch to each of recipients
               * @param recipients The list of recipients to send to
               * @param values The list of values to send to recipients.
               *  The recipient with index i in recipients array will be sent values[i].
               *  Thus, recipients and values must be the same length
               */
              function batchTransfer(
                address[] calldata recipients,
                uint256[] calldata values
              ) internal {
                for (uint256 i = 0; i < recipients.length; i++) {
                  require(address(this).balance >= values[i], 'Insufficient funds');
                  (bool success, ) = recipients[i].call{ value: values[i] }('');
                  require(success, 'Call failed');
                  emit BatchTransfer(msg.sender, recipients[i], values[i]);
                }
              }
              /**
               * Execute a multi-signature token transfer from this wallet using 2 signers: one from msg.sender and the other from ecrecover.
               * Sequence IDs are numbers starting from 1. They are used to prevent replay attacks and may not be repeated.
               *
               * @param toAddress the destination address to send an outgoing transaction
               * @param value the amount in tokens to be sent
               * @param tokenContractAddress the address of the erc20 token contract
               * @param expireTime the number of seconds since 1970 for which this transaction is valid
               * @param sequenceId the unique sequence id obtainable from getNextSequenceId
               * @param signature see Data Formats
               */
              function sendMultiSigToken(
                address toAddress,
                uint256 value,
                address tokenContractAddress,
                uint256 expireTime,
                uint256 sequenceId,
                bytes calldata signature
              ) external onlySigner {
                // Verify the other signer
                bytes32 operationHash = keccak256(
                  abi.encodePacked(
                    getTokenNetworkId(),
                    toAddress,
                    value,
                    tokenContractAddress,
                    expireTime,
                    sequenceId
                  )
                );
                verifyMultiSig(toAddress, operationHash, signature, expireTime, sequenceId);
                TransferHelper.safeTransfer(tokenContractAddress, toAddress, value);
              }
              /**
               * Execute a token flush from one of the forwarder addresses. This transfer needs only a single signature and can be done by any signer
               *
               * @param forwarderAddress the address of the forwarder address to flush the tokens from
               * @param tokenContractAddress the address of the erc20 token contract
               */
              function flushForwarderTokens(
                address payable forwarderAddress,
                address tokenContractAddress
              ) external onlySigner {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.flushTokens(tokenContractAddress);
              }
              /**
               * Execute a ERC721 token flush from one of the forwarder addresses. This transfer needs only a single signature and can be done by any signer
               *
               * @param forwarderAddress the address of the forwarder address to flush the tokens from
               * @param tokenContractAddress the address of the erc20 token contract
               */
              function flushERC721ForwarderTokens(
                address payable forwarderAddress,
                address tokenContractAddress,
                uint256 tokenId
              ) external onlySigner {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.flushERC721Token(tokenContractAddress, tokenId);
              }
              /**
               * Execute a ERC1155 batch token flush from one of the forwarder addresses.
               * This transfer needs only a single signature and can be done by any signer.
               *
               * @param forwarderAddress the address of the forwarder address to flush the tokens from
               * @param tokenContractAddress the address of the erc1155 token contract
               */
              function batchFlushERC1155ForwarderTokens(
                address payable forwarderAddress,
                address tokenContractAddress,
                uint256[] calldata tokenIds
              ) external onlySigner {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.batchFlushERC1155Tokens(tokenContractAddress, tokenIds);
              }
              /**
               * Execute a ERC1155 token flush from one of the forwarder addresses.
               * This transfer needs only a single signature and can be done by any signer.
               *
               * @param forwarderAddress the address of the forwarder address to flush the tokens from
               * @param tokenContractAddress the address of the erc1155 token contract
               * @param tokenId the token id associated with the ERC1155
               */
              function flushERC1155ForwarderTokens(
                address payable forwarderAddress,
                address tokenContractAddress,
                uint256 tokenId
              ) external onlySigner {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.flushERC1155Tokens(tokenContractAddress, tokenId);
              }
              /**
               * Sets the autoflush 721 parameter on the forwarder.
               *
               * @param forwarderAddress the address of the forwarder to toggle.
               * @param autoFlush whether to autoflush erc721 tokens
               */
              function setAutoFlush721(address forwarderAddress, bool autoFlush)
                external
                onlySigner
              {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.setAutoFlush721(autoFlush);
              }
              /**
               * Sets the autoflush 721 parameter on the forwarder.
               *
               * @param forwarderAddress the address of the forwarder to toggle.
               * @param autoFlush whether to autoflush erc1155 tokens
               */
              function setAutoFlush1155(address forwarderAddress, bool autoFlush)
                external
                onlySigner
              {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.setAutoFlush1155(autoFlush);
              }
              /**
               * Do common multisig verification for both eth sends and erc20token transfers
               *
               * @param toAddress the destination address to send an outgoing transaction
               * @param operationHash see Data Formats
               * @param signature see Data Formats
               * @param expireTime the number of seconds since 1970 for which this transaction is valid
               * @param sequenceId the unique sequence id obtainable from getNextSequenceId
               * returns address that has created the signature
               */
              function verifyMultiSig(
                address toAddress,
                bytes32 operationHash,
                bytes calldata signature,
                uint256 expireTime,
                uint256 sequenceId
              ) private returns (address) {
                address otherSigner = recoverAddressFromSignature(operationHash, signature);
                // Verify if we are in safe mode. In safe mode, the wallet can only send to signers
                require(!safeMode || isSigner(toAddress), 'External transfer in safe mode');
                // Verify that the transaction has not expired
                require(expireTime >= block.timestamp, 'Transaction expired');
                // Try to insert the sequence ID. Will revert if the sequence id was invalid
                tryInsertSequenceId(sequenceId);
                require(isSigner(otherSigner), 'Invalid signer');
                require(otherSigner != msg.sender, 'Signers cannot be equal');
                return otherSigner;
              }
              /**
               * ERC721 standard callback function for when a ERC721 is transfered.
               *
               * @param _operator The address of the nft contract
               * @param _from The address of the sender
               * @param _tokenId The token id of the nft
               * @param _data Additional data with no specified format, sent in call to `_to`
               */
              function onERC721Received(
                address _operator,
                address _from,
                uint256 _tokenId,
                bytes memory _data
              ) external virtual override returns (bytes4) {
                return this.onERC721Received.selector;
              }
              /**
               * @inheritdoc IERC1155Receiver
               */
              function onERC1155Received(
                address _operator,
                address _from,
                uint256 id,
                uint256 value,
                bytes calldata data
              ) external virtual override returns (bytes4) {
                return this.onERC1155Received.selector;
              }
              /**
               * @inheritdoc IERC1155Receiver
               */
              function onERC1155BatchReceived(
                address _operator,
                address _from,
                uint256[] calldata ids,
                uint256[] calldata values,
                bytes calldata data
              ) external virtual override returns (bytes4) {
                return this.onERC1155BatchReceived.selector;
              }
              /**
               * Irrevocably puts contract into safe mode. When in this mode, transactions may only be sent to signing addresses.
               */
              function activateSafeMode() external onlySigner {
                safeMode = true;
                emit SafeModeActivated(msg.sender);
              }
              /**
               * Gets signer's address using ecrecover
               * @param operationHash see Data Formats
               * @param signature see Data Formats
               * returns address recovered from the signature
               */
              function recoverAddressFromSignature(
                bytes32 operationHash,
                bytes memory signature
              ) private pure returns (address) {
                require(signature.length == 65, 'Invalid signature - wrong length');
                // We need to unpack the signature, which is given as an array of 65 bytes (like eth.sign)
                bytes32 r;
                bytes32 s;
                uint8 v;
                // solhint-disable-next-line
                assembly {
                  r := mload(add(signature, 32))
                  s := mload(add(signature, 64))
                  v := and(mload(add(signature, 65)), 255)
                }
                if (v < 27) {
                  v += 27; // Ethereum versions are 27 or 28 as opposed to 0 or 1 which is submitted by some signing libs
                }
                // protect against signature malleability
                // S value must be in the lower half orader
                // reference: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/051d340171a93a3d401aaaea46b4b62fa81e5d7c/contracts/cryptography/ECDSA.sol#L53
                require(
                  uint256(s) <=
                    0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
                  "ECDSA: invalid signature 's' value"
                );
                // note that this returns 0 if the signature is invalid
                // Since 0x0 can never be a signer, when the recovered signer address
                // is checked against our signer list, that 0x0 will cause an invalid signer failure
                return ecrecover(operationHash, v, r, s);
              }
              /**
               * Verify that the sequence id has not been used before and inserts it. Throws if the sequence ID was not accepted.
               * We collect a window of up to 10 recent sequence ids, and allow any sequence id that is not in the window and
               * greater than the minimum element in the window.
               * @param sequenceId to insert into array of stored ids
               */
              function tryInsertSequenceId(uint256 sequenceId) private onlySigner {
                // Keep a pointer to the lowest value element in the window
                uint256 lowestValueIndex = 0;
                // fetch recentSequenceIds into memory for function context to avoid unnecessary sloads
                  uint256[SEQUENCE_ID_WINDOW_SIZE] memory _recentSequenceIds
                 = recentSequenceIds;
                for (uint256 i = 0; i < SEQUENCE_ID_WINDOW_SIZE; i++) {
                  require(_recentSequenceIds[i] != sequenceId, 'Sequence ID already used');
                  if (_recentSequenceIds[i] < _recentSequenceIds[lowestValueIndex]) {
                    lowestValueIndex = i;
                  }
                }
                // The sequence ID being used is lower than the lowest value in the window
                // so we cannot accept it as it may have been used before
                require(
                  sequenceId > _recentSequenceIds[lowestValueIndex],
                  'Sequence ID below window'
                );
                // Block sequence IDs which are much higher than the lowest value
                // This prevents people blocking the contract by using very large sequence IDs quickly
                require(
                  sequenceId <=
                    (_recentSequenceIds[lowestValueIndex] + MAX_SEQUENCE_ID_INCREASE),
                  'Sequence ID above maximum'
                );
                recentSequenceIds[lowestValueIndex] = sequenceId;
              }
              /**
               * Gets the next available sequence ID for signing when using executeAndConfirm
               * returns the sequenceId one higher than the highest currently stored
               */
              function getNextSequenceId() external view returns (uint256) {
                uint256 highestSequenceId = 0;
                for (uint256 i = 0; i < SEQUENCE_ID_WINDOW_SIZE; i++) {
                  if (recentSequenceIds[i] > highestSequenceId) {
                    highestSequenceId = recentSequenceIds[i];
                  }
                }
                return highestSequenceId + 1;
              }
            }
            // SPDX-License-Identifier: GPL-3.0-or-later
            // source: https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/TransferHelper.sol
            pragma solidity 0.8.10;
            import '@openzeppelin/contracts/utils/Address.sol';
            // helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
            library TransferHelper {
              function safeTransfer(
                address token,
                address to,
                uint256 value
              ) internal {
                // bytes4(keccak256(bytes('transfer(address,uint256)')));
                (bool success, bytes memory data) = token.call(
                  abi.encodeWithSelector(0xa9059cbb, to, value)
                );
                require(
                  success && (data.length == 0 || abi.decode(data, (bool))),
                  'TransferHelper::safeTransfer: transfer failed'
                );
              }
              function safeTransferFrom(
                address token,
                address from,
                address to,
                uint256 value
              ) internal {
                // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
                (bool success, bytes memory returndata) = token.call(
                  abi.encodeWithSelector(0x23b872dd, from, to, value)
                );
                Address.verifyCallResult(
                  success,
                  returndata,
                  'TransferHelper::transferFrom: transferFrom failed'
                );
              }
            }
            // SPDX-License-Identifier: UNLICENSED
            pragma solidity 0.8.10;
            /**
             * Contract that exposes the needed erc20 token functions
             */
            abstract contract ERC20Interface {
              // Send _value amount of tokens to address _to
              function transfer(address _to, uint256 _value)
                public
                virtual
                returns (bool success);
              // Get the account balance of another account with address _owner
              function balanceOf(address _owner)
                public
                virtual
                view
                returns (uint256 balance);
            }
            pragma solidity ^0.8.0;
            import '@openzeppelin/contracts/utils/introspection/IERC165.sol';
            interface IForwarder is IERC165 {
              /**
               * Sets the autoflush721 parameter.
               *
               * @param autoFlush whether to autoflush erc721 tokens
               */
              function setAutoFlush721(bool autoFlush) external;
              /**
               * Sets the autoflush1155 parameter.
               *
               * @param autoFlush whether to autoflush erc1155 tokens
               */
              function setAutoFlush1155(bool autoFlush) external;
              /**
               * Execute a token transfer of the full balance from the forwarder token to the parent address
               *
               * @param tokenContractAddress the address of the erc20 token contract
               */
              function flushTokens(address tokenContractAddress) external;
              /**
               * Execute a nft transfer from the forwarder to the parent address
               *
               * @param tokenContractAddress the address of the ERC721 NFT contract
               * @param tokenId The token id of the nft
               */
              function flushERC721Token(address tokenContractAddress, uint256 tokenId)
                external;
              /**
               * Execute a nft transfer from the forwarder to the parent address.
               *
               * @param tokenContractAddress the address of the ERC1155 NFT contract
               * @param tokenId The token id of the nft
               */
              function flushERC1155Tokens(address tokenContractAddress, uint256 tokenId)
                external;
              /**
               * Execute a batch nft transfer from the forwarder to the parent address.
               *
               * @param tokenContractAddress the address of the ERC1155 NFT contract
               * @param tokenIds The token ids of the nfts
               */
              function batchFlushERC1155Tokens(
                address tokenContractAddress,
                uint256[] calldata tokenIds
              ) external;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)
            pragma solidity ^0.8.0;
            /**
             * @title ERC721 token receiver interface
             * @dev Interface for any contract that wants to support safeTransfers
             * from ERC721 asset contracts.
             */
            interface IERC721Receiver {
                /**
                 * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
                 * by `operator` from `from`, this function is called.
                 *
                 * It must return its Solidity selector to confirm the token transfer.
                 * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
                 *
                 * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
                 */
                function onERC721Received(
                    address operator,
                    address from,
                    uint256 tokenId,
                    bytes calldata data
                ) external returns (bytes4);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC1155/utils/ERC1155Receiver.sol)
            pragma solidity ^0.8.0;
            import "../IERC1155Receiver.sol";
            import "../../../utils/introspection/ERC165.sol";
            /**
             * @dev _Available since v3.1._
             */
            abstract contract ERC1155Receiver is ERC165, IERC1155Receiver {
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
                    return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/Address.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Collection of functions related to the address type
             */
            library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // This method relies on extcodesize, which returns 0 for contracts in
                    // construction, since the code is only stored at the end of the
                    // constructor execution.
                    uint256 size;
                    assembly {
                        size := extcodesize(account)
                    }
                    return size > 0;
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
                    (bool success, ) = recipient.call{value: amount}("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain `call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCall(target, data, "Address: low-level call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                 * `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, 0, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but also transferring `value` wei to `target`.
                 *
                 * Requirements:
                 *
                 * - the calling contract must have an ETH balance of at least `value`.
                 * - the called Solidity function must be `payable`.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(address(this).balance >= value, "Address: insufficient balance for call");
                    require(isContract(target), "Address: call to non-contract");
                    (bool success, bytes memory returndata) = target.call{value: value}(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                    return functionStaticCall(target, data, "Address: low-level static call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    require(isContract(target), "Address: static call to non-contract");
                    (bool success, bytes memory returndata) = target.staticcall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(isContract(target), "Address: delegate call to non-contract");
                    (bool success, bytes memory returndata) = target.delegatecall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                 * revert reason using the provided one.
                 *
                 * _Available since v4.3._
                 */
                function verifyCallResult(
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal pure returns (bytes memory) {
                    if (success) {
                        return returndata;
                    } else {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Interface of the ERC165 standard, as defined in the
             * https://eips.ethereum.org/EIPS/eip-165[EIP].
             *
             * Implementers can declare support of contract interfaces, which can then be
             * queried by others ({ERC165Checker}).
             *
             * For an implementation, see {ERC165}.
             */
            interface IERC165 {
                /**
                 * @dev Returns true if this contract implements the interface defined by
                 * `interfaceId`. See the corresponding
                 * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
                 * to learn more about how these ids are created.
                 *
                 * This function call must use less than 30 000 gas.
                 */
                function supportsInterface(bytes4 interfaceId) external view returns (bool);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC1155/IERC1155Receiver.sol)
            pragma solidity ^0.8.0;
            import "../../utils/introspection/IERC165.sol";
            /**
             * @dev _Available since v3.1._
             */
            interface IERC1155Receiver is IERC165 {
                /**
                    @dev Handles the receipt of a single ERC1155 token type. This function is
                    called at the end of a `safeTransferFrom` after the balance has been updated.
                    To accept the transfer, this must return
                    `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
                    (i.e. 0xf23a6e61, or its own function selector).
                    @param operator The address which initiated the transfer (i.e. msg.sender)
                    @param from The address which previously owned the token
                    @param id The ID of the token being transferred
                    @param value The amount of tokens being transferred
                    @param data Additional data with no specified format
                    @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
                */
                function onERC1155Received(
                    address operator,
                    address from,
                    uint256 id,
                    uint256 value,
                    bytes calldata data
                ) external returns (bytes4);
                /**
                    @dev Handles the receipt of a multiple ERC1155 token types. This function
                    is called at the end of a `safeBatchTransferFrom` after the balances have
                    been updated. To accept the transfer(s), this must return
                    `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
                    (i.e. 0xbc197c81, or its own function selector).
                    @param operator The address which initiated the batch transfer (i.e. msg.sender)
                    @param from The address which previously owned the token
                    @param ids An array containing ids of each token being transferred (order and length must match values array)
                    @param values An array containing amounts of each token being transferred (order and length must match ids array)
                    @param data Additional data with no specified format
                    @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
                */
                function onERC1155BatchReceived(
                    address operator,
                    address from,
                    uint256[] calldata ids,
                    uint256[] calldata values,
                    bytes calldata data
                ) external returns (bytes4);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
            pragma solidity ^0.8.0;
            import "./IERC165.sol";
            /**
             * @dev Implementation of the {IERC165} interface.
             *
             * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
             * for the additional interface id that will be supported. For example:
             *
             * ```solidity
             * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
             *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
             * }
             * ```
             *
             * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
             */
            abstract contract ERC165 is IERC165 {
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                    return interfaceId == type(IERC165).interfaceId;
                }
            }
            

            File 2 of 6: Forwarder
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity 0.8.10;
            import '@openzeppelin/contracts/token/ERC1155/IERC1155.sol';
            import '@openzeppelin/contracts/token/ERC721/IERC721.sol';
            import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol';
            import '@openzeppelin/contracts/token/ERC1155/utils/ERC1155Receiver.sol';
            import './ERC20Interface.sol';
            import './TransferHelper.sol';
            import './IForwarder.sol';
            /**
             * Contract that will forward any incoming Ether to the creator of the contract
             *
             */
            contract Forwarder is IERC721Receiver, ERC1155Receiver, IForwarder {
              // Address to which any funds sent to this contract will be forwarded
              address public parentAddress;
              bool public autoFlush721 = true;
              bool public autoFlush1155 = true;
              event ForwarderDeposited(address from, uint256 value, bytes data);
              /**
               * Initialize the contract, and sets the destination address to that of the creator
               */
              function init(
                address _parentAddress,
                bool _autoFlush721,
                bool _autoFlush1155
              ) external onlyUninitialized {
                parentAddress = _parentAddress;
                uint256 value = address(this).balance;
                // set whether we want to automatically flush erc721/erc1155 tokens or not
                autoFlush721 = _autoFlush721;
                autoFlush1155 = _autoFlush1155;
                if (value == 0) {
                  return;
                }
                (bool success, ) = parentAddress.call{ value: value }('');
                require(success, 'Flush failed');
                // NOTE: since we are forwarding on initialization,
                // we don't have the context of the original sender.
                // We still emit an event about the forwarding but set
                // the sender to the forwarder itself
                emit ForwarderDeposited(address(this), value, msg.data);
              }
              /**
               * Modifier that will execute internal code block only if the sender is the parent address
               */
              modifier onlyParent {
                require(msg.sender == parentAddress, 'Only Parent');
                _;
              }
              /**
               * Modifier that will execute internal code block only if the contract has not been initialized yet
               */
              modifier onlyUninitialized {
                require(parentAddress == address(0x0), 'Already initialized');
                _;
              }
              /**
               * Default function; Gets called when data is sent but does not match any other function
               */
              fallback() external payable {
                flush();
              }
              /**
               * Default function; Gets called when Ether is deposited with no data, and forwards it to the parent address
               */
              receive() external payable {
                flush();
              }
              /**
               * @inheritdoc IForwarder
               */
              function setAutoFlush721(bool autoFlush)
                external
                virtual
                override
                onlyParent
              {
                autoFlush721 = autoFlush;
              }
              /**
               * @inheritdoc IForwarder
               */
              function setAutoFlush1155(bool autoFlush)
                external
                virtual
                override
                onlyParent
              {
                autoFlush1155 = autoFlush;
              }
              /**
               * ERC721 standard callback function for when a ERC721 is transfered. The forwarder will send the nft
               * to the base wallet once the nft contract invokes this method after transfering the nft.
               *
               * @param _operator The address which called `safeTransferFrom` function
               * @param _from The address of the sender
               * @param _tokenId The token id of the nft
               * @param data Additional data with no specified format, sent in call to `_to`
               */
              function onERC721Received(
                address _operator,
                address _from,
                uint256 _tokenId,
                bytes memory data
              ) external virtual override returns (bytes4) {
                if (autoFlush721) {
                  IERC721 instance = IERC721(msg.sender);
                  require(
                    instance.supportsInterface(type(IERC721).interfaceId),
                    'The caller does not support the ERC721 interface'
                  );
                  // this won't work for ERC721 re-entrancy
                  instance.safeTransferFrom(address(this), parentAddress, _tokenId, data);
                }
                return this.onERC721Received.selector;
              }
              function callFromParent(
                address target,
                uint256 value,
                bytes calldata data
              ) external onlyParent returns (bytes memory) {
                (bool success, bytes memory returnedData) = target.call{ value: value }(
                  data
                );
                require(success, 'Parent call execution failed');
                return returnedData;
              }
              /**
               * @inheritdoc IERC1155Receiver
               */
              function onERC1155Received(
                address _operator,
                address _from,
                uint256 id,
                uint256 value,
                bytes calldata data
              ) external virtual override returns (bytes4) {
                IERC1155 instance = IERC1155(msg.sender);
                require(
                  instance.supportsInterface(type(IERC1155).interfaceId),
                  'The caller does not support the IERC1155 interface'
                );
                if (autoFlush1155) {
                  instance.safeTransferFrom(address(this), parentAddress, id, value, data);
                }
                return this.onERC1155Received.selector;
              }
              /**
               * @inheritdoc IERC1155Receiver
               */
              function onERC1155BatchReceived(
                address _operator,
                address _from,
                uint256[] calldata ids,
                uint256[] calldata values,
                bytes calldata data
              ) external virtual override returns (bytes4) {
                IERC1155 instance = IERC1155(msg.sender);
                require(
                  instance.supportsInterface(type(IERC1155).interfaceId),
                  'The caller does not support the IERC1155 interface'
                );
                if (autoFlush1155) {
                  instance.safeBatchTransferFrom(
                    address(this),
                    parentAddress,
                    ids,
                    values,
                    data
                  );
                }
                return this.onERC1155BatchReceived.selector;
              }
              /**
               * @inheritdoc IForwarder
               */
              function flushTokens(address tokenContractAddress)
                external
                virtual
                override
                onlyParent
              {
                ERC20Interface instance = ERC20Interface(tokenContractAddress);
                address forwarderAddress = address(this);
                uint256 forwarderBalance = instance.balanceOf(forwarderAddress);
                if (forwarderBalance == 0) {
                  return;
                }
                TransferHelper.safeTransfer(
                  tokenContractAddress,
                  parentAddress,
                  forwarderBalance
                );
              }
              /**
               * @inheritdoc IForwarder
               */
              function flushERC721Token(address tokenContractAddress, uint256 tokenId)
                external
                virtual
                override
                onlyParent
              {
                IERC721 instance = IERC721(tokenContractAddress);
                require(
                  instance.supportsInterface(type(IERC721).interfaceId),
                  'The tokenContractAddress does not support the ERC721 interface'
                );
                address ownerAddress = instance.ownerOf(tokenId);
                instance.transferFrom(ownerAddress, parentAddress, tokenId);
              }
              /**
               * @inheritdoc IForwarder
               */
              function flushERC1155Tokens(address tokenContractAddress, uint256 tokenId)
                external
                virtual
                override
                onlyParent
              {
                IERC1155 instance = IERC1155(tokenContractAddress);
                require(
                  instance.supportsInterface(type(IERC1155).interfaceId),
                  'The caller does not support the IERC1155 interface'
                );
                address forwarderAddress = address(this);
                uint256 forwarderBalance = instance.balanceOf(forwarderAddress, tokenId);
                instance.safeTransferFrom(
                  forwarderAddress,
                  parentAddress,
                  tokenId,
                  forwarderBalance,
                  ''
                );
              }
              /**
               * @inheritdoc IForwarder
               */
              function batchFlushERC1155Tokens(
                address tokenContractAddress,
                uint256[] calldata tokenIds
              ) external virtual override onlyParent {
                IERC1155 instance = IERC1155(tokenContractAddress);
                require(
                  instance.supportsInterface(type(IERC1155).interfaceId),
                  'The caller does not support the IERC1155 interface'
                );
                address forwarderAddress = address(this);
                uint256[] memory amounts = new uint256[](tokenIds.length);
                for (uint256 i = 0; i < tokenIds.length; i++) {
                  amounts[i] = instance.balanceOf(forwarderAddress, tokenIds[i]);
                }
                instance.safeBatchTransferFrom(
                  forwarderAddress,
                  parentAddress,
                  tokenIds,
                  amounts,
                  ''
                );
              }
              /**
               * Flush the entire balance of the contract to the parent address.
               */
              function flush() public {
                uint256 value = address(this).balance;
                if (value == 0) {
                  return;
                }
                (bool success, ) = parentAddress.call{ value: value }('');
                require(success, 'Flush failed');
                emit ForwarderDeposited(msg.sender, value, msg.data);
              }
              /**
               * @inheritdoc IERC165
               */
              function supportsInterface(bytes4 interfaceId)
                public
                virtual
                override(ERC1155Receiver, IERC165)
                view
                returns (bool)
              {
                return
                  interfaceId == type(IForwarder).interfaceId ||
                  super.supportsInterface(interfaceId);
              }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC1155/IERC1155.sol)
            pragma solidity ^0.8.0;
            import "../../utils/introspection/IERC165.sol";
            /**
             * @dev Required interface of an ERC1155 compliant contract, as defined in the
             * https://eips.ethereum.org/EIPS/eip-1155[EIP].
             *
             * _Available since v3.1._
             */
            interface IERC1155 is IERC165 {
                /**
                 * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
                 */
                event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
                /**
                 * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
                 * transfers.
                 */
                event TransferBatch(
                    address indexed operator,
                    address indexed from,
                    address indexed to,
                    uint256[] ids,
                    uint256[] values
                );
                /**
                 * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
                 * `approved`.
                 */
                event ApprovalForAll(address indexed account, address indexed operator, bool approved);
                /**
                 * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
                 *
                 * If an {URI} event was emitted for `id`, the standard
                 * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
                 * returned by {IERC1155MetadataURI-uri}.
                 */
                event URI(string value, uint256 indexed id);
                /**
                 * @dev Returns the amount of tokens of token type `id` owned by `account`.
                 *
                 * Requirements:
                 *
                 * - `account` cannot be the zero address.
                 */
                function balanceOf(address account, uint256 id) external view returns (uint256);
                /**
                 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
                 *
                 * Requirements:
                 *
                 * - `accounts` and `ids` must have the same length.
                 */
                function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
                    external
                    view
                    returns (uint256[] memory);
                /**
                 * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
                 *
                 * Emits an {ApprovalForAll} event.
                 *
                 * Requirements:
                 *
                 * - `operator` cannot be the caller.
                 */
                function setApprovalForAll(address operator, bool approved) external;
                /**
                 * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
                 *
                 * See {setApprovalForAll}.
                 */
                function isApprovedForAll(address account, address operator) external view returns (bool);
                /**
                 * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
                 *
                 * Emits a {TransferSingle} event.
                 *
                 * Requirements:
                 *
                 * - `to` cannot be the zero address.
                 * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
                 * - `from` must have a balance of tokens of type `id` of at least `amount`.
                 * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
                 * acceptance magic value.
                 */
                function safeTransferFrom(
                    address from,
                    address to,
                    uint256 id,
                    uint256 amount,
                    bytes calldata data
                ) external;
                /**
                 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
                 *
                 * Emits a {TransferBatch} event.
                 *
                 * Requirements:
                 *
                 * - `ids` and `amounts` must have the same length.
                 * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
                 * acceptance magic value.
                 */
                function safeBatchTransferFrom(
                    address from,
                    address to,
                    uint256[] calldata ids,
                    uint256[] calldata amounts,
                    bytes calldata data
                ) external;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)
            pragma solidity ^0.8.0;
            import "../../utils/introspection/IERC165.sol";
            /**
             * @dev Required interface of an ERC721 compliant contract.
             */
            interface IERC721 is IERC165 {
                /**
                 * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
                 */
                event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                /**
                 * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
                 */
                event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
                /**
                 * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
                 */
                event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
                /**
                 * @dev Returns the number of tokens in ``owner``'s account.
                 */
                function balanceOf(address owner) external view returns (uint256 balance);
                /**
                 * @dev Returns the owner of the `tokenId` token.
                 *
                 * Requirements:
                 *
                 * - `tokenId` must exist.
                 */
                function ownerOf(uint256 tokenId) external view returns (address owner);
                /**
                 * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
                 * are aware of the ERC721 protocol to prevent tokens from being forever locked.
                 *
                 * Requirements:
                 *
                 * - `from` cannot be the zero address.
                 * - `to` cannot be the zero address.
                 * - `tokenId` token must exist and be owned by `from`.
                 * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
                 * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                 *
                 * Emits a {Transfer} event.
                 */
                function safeTransferFrom(
                    address from,
                    address to,
                    uint256 tokenId
                ) external;
                /**
                 * @dev Transfers `tokenId` token from `from` to `to`.
                 *
                 * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
                 *
                 * Requirements:
                 *
                 * - `from` cannot be the zero address.
                 * - `to` cannot be the zero address.
                 * - `tokenId` token must be owned by `from`.
                 * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                 *
                 * Emits a {Transfer} event.
                 */
                function transferFrom(
                    address from,
                    address to,
                    uint256 tokenId
                ) external;
                /**
                 * @dev Gives permission to `to` to transfer `tokenId` token to another account.
                 * The approval is cleared when the token is transferred.
                 *
                 * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
                 *
                 * Requirements:
                 *
                 * - The caller must own the token or be an approved operator.
                 * - `tokenId` must exist.
                 *
                 * Emits an {Approval} event.
                 */
                function approve(address to, uint256 tokenId) external;
                /**
                 * @dev Returns the account approved for `tokenId` token.
                 *
                 * Requirements:
                 *
                 * - `tokenId` must exist.
                 */
                function getApproved(uint256 tokenId) external view returns (address operator);
                /**
                 * @dev Approve or remove `operator` as an operator for the caller.
                 * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
                 *
                 * Requirements:
                 *
                 * - The `operator` cannot be the caller.
                 *
                 * Emits an {ApprovalForAll} event.
                 */
                function setApprovalForAll(address operator, bool _approved) external;
                /**
                 * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
                 *
                 * See {setApprovalForAll}
                 */
                function isApprovedForAll(address owner, address operator) external view returns (bool);
                /**
                 * @dev Safely transfers `tokenId` token from `from` to `to`.
                 *
                 * Requirements:
                 *
                 * - `from` cannot be the zero address.
                 * - `to` cannot be the zero address.
                 * - `tokenId` token must exist and be owned by `from`.
                 * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                 * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                 *
                 * Emits a {Transfer} event.
                 */
                function safeTransferFrom(
                    address from,
                    address to,
                    uint256 tokenId,
                    bytes calldata data
                ) external;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)
            pragma solidity ^0.8.0;
            /**
             * @title ERC721 token receiver interface
             * @dev Interface for any contract that wants to support safeTransfers
             * from ERC721 asset contracts.
             */
            interface IERC721Receiver {
                /**
                 * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
                 * by `operator` from `from`, this function is called.
                 *
                 * It must return its Solidity selector to confirm the token transfer.
                 * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
                 *
                 * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
                 */
                function onERC721Received(
                    address operator,
                    address from,
                    uint256 tokenId,
                    bytes calldata data
                ) external returns (bytes4);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC1155/utils/ERC1155Receiver.sol)
            pragma solidity ^0.8.0;
            import "../IERC1155Receiver.sol";
            import "../../../utils/introspection/ERC165.sol";
            /**
             * @dev _Available since v3.1._
             */
            abstract contract ERC1155Receiver is ERC165, IERC1155Receiver {
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
                    return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
                }
            }
            // SPDX-License-Identifier: UNLICENSED
            pragma solidity 0.8.10;
            /**
             * Contract that exposes the needed erc20 token functions
             */
            abstract contract ERC20Interface {
              // Send _value amount of tokens to address _to
              function transfer(address _to, uint256 _value)
                public
                virtual
                returns (bool success);
              // Get the account balance of another account with address _owner
              function balanceOf(address _owner)
                public
                virtual
                view
                returns (uint256 balance);
            }
            // SPDX-License-Identifier: GPL-3.0-or-later
            // source: https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/TransferHelper.sol
            pragma solidity 0.8.10;
            import '@openzeppelin/contracts/utils/Address.sol';
            // helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
            library TransferHelper {
              function safeTransfer(
                address token,
                address to,
                uint256 value
              ) internal {
                // bytes4(keccak256(bytes('transfer(address,uint256)')));
                (bool success, bytes memory data) = token.call(
                  abi.encodeWithSelector(0xa9059cbb, to, value)
                );
                require(
                  success && (data.length == 0 || abi.decode(data, (bool))),
                  'TransferHelper::safeTransfer: transfer failed'
                );
              }
              function safeTransferFrom(
                address token,
                address from,
                address to,
                uint256 value
              ) internal {
                // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
                (bool success, bytes memory returndata) = token.call(
                  abi.encodeWithSelector(0x23b872dd, from, to, value)
                );
                Address.verifyCallResult(
                  success,
                  returndata,
                  'TransferHelper::transferFrom: transferFrom failed'
                );
              }
            }
            pragma solidity ^0.8.0;
            import '@openzeppelin/contracts/utils/introspection/IERC165.sol';
            interface IForwarder is IERC165 {
              /**
               * Sets the autoflush721 parameter.
               *
               * @param autoFlush whether to autoflush erc721 tokens
               */
              function setAutoFlush721(bool autoFlush) external;
              /**
               * Sets the autoflush1155 parameter.
               *
               * @param autoFlush whether to autoflush erc1155 tokens
               */
              function setAutoFlush1155(bool autoFlush) external;
              /**
               * Execute a token transfer of the full balance from the forwarder token to the parent address
               *
               * @param tokenContractAddress the address of the erc20 token contract
               */
              function flushTokens(address tokenContractAddress) external;
              /**
               * Execute a nft transfer from the forwarder to the parent address
               *
               * @param tokenContractAddress the address of the ERC721 NFT contract
               * @param tokenId The token id of the nft
               */
              function flushERC721Token(address tokenContractAddress, uint256 tokenId)
                external;
              /**
               * Execute a nft transfer from the forwarder to the parent address.
               *
               * @param tokenContractAddress the address of the ERC1155 NFT contract
               * @param tokenId The token id of the nft
               */
              function flushERC1155Tokens(address tokenContractAddress, uint256 tokenId)
                external;
              /**
               * Execute a batch nft transfer from the forwarder to the parent address.
               *
               * @param tokenContractAddress the address of the ERC1155 NFT contract
               * @param tokenIds The token ids of the nfts
               */
              function batchFlushERC1155Tokens(
                address tokenContractAddress,
                uint256[] calldata tokenIds
              ) external;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Interface of the ERC165 standard, as defined in the
             * https://eips.ethereum.org/EIPS/eip-165[EIP].
             *
             * Implementers can declare support of contract interfaces, which can then be
             * queried by others ({ERC165Checker}).
             *
             * For an implementation, see {ERC165}.
             */
            interface IERC165 {
                /**
                 * @dev Returns true if this contract implements the interface defined by
                 * `interfaceId`. See the corresponding
                 * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
                 * to learn more about how these ids are created.
                 *
                 * This function call must use less than 30 000 gas.
                 */
                function supportsInterface(bytes4 interfaceId) external view returns (bool);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC1155/IERC1155Receiver.sol)
            pragma solidity ^0.8.0;
            import "../../utils/introspection/IERC165.sol";
            /**
             * @dev _Available since v3.1._
             */
            interface IERC1155Receiver is IERC165 {
                /**
                    @dev Handles the receipt of a single ERC1155 token type. This function is
                    called at the end of a `safeTransferFrom` after the balance has been updated.
                    To accept the transfer, this must return
                    `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
                    (i.e. 0xf23a6e61, or its own function selector).
                    @param operator The address which initiated the transfer (i.e. msg.sender)
                    @param from The address which previously owned the token
                    @param id The ID of the token being transferred
                    @param value The amount of tokens being transferred
                    @param data Additional data with no specified format
                    @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
                */
                function onERC1155Received(
                    address operator,
                    address from,
                    uint256 id,
                    uint256 value,
                    bytes calldata data
                ) external returns (bytes4);
                /**
                    @dev Handles the receipt of a multiple ERC1155 token types. This function
                    is called at the end of a `safeBatchTransferFrom` after the balances have
                    been updated. To accept the transfer(s), this must return
                    `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
                    (i.e. 0xbc197c81, or its own function selector).
                    @param operator The address which initiated the batch transfer (i.e. msg.sender)
                    @param from The address which previously owned the token
                    @param ids An array containing ids of each token being transferred (order and length must match values array)
                    @param values An array containing amounts of each token being transferred (order and length must match ids array)
                    @param data Additional data with no specified format
                    @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
                */
                function onERC1155BatchReceived(
                    address operator,
                    address from,
                    uint256[] calldata ids,
                    uint256[] calldata values,
                    bytes calldata data
                ) external returns (bytes4);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
            pragma solidity ^0.8.0;
            import "./IERC165.sol";
            /**
             * @dev Implementation of the {IERC165} interface.
             *
             * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
             * for the additional interface id that will be supported. For example:
             *
             * ```solidity
             * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
             *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
             * }
             * ```
             *
             * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
             */
            abstract contract ERC165 is IERC165 {
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                    return interfaceId == type(IERC165).interfaceId;
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/Address.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Collection of functions related to the address type
             */
            library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // This method relies on extcodesize, which returns 0 for contracts in
                    // construction, since the code is only stored at the end of the
                    // constructor execution.
                    uint256 size;
                    assembly {
                        size := extcodesize(account)
                    }
                    return size > 0;
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
                    (bool success, ) = recipient.call{value: amount}("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain `call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCall(target, data, "Address: low-level call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                 * `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, 0, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but also transferring `value` wei to `target`.
                 *
                 * Requirements:
                 *
                 * - the calling contract must have an ETH balance of at least `value`.
                 * - the called Solidity function must be `payable`.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(address(this).balance >= value, "Address: insufficient balance for call");
                    require(isContract(target), "Address: call to non-contract");
                    (bool success, bytes memory returndata) = target.call{value: value}(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                    return functionStaticCall(target, data, "Address: low-level static call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    require(isContract(target), "Address: static call to non-contract");
                    (bool success, bytes memory returndata) = target.staticcall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(isContract(target), "Address: delegate call to non-contract");
                    (bool success, bytes memory returndata) = target.delegatecall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                 * revert reason using the provided one.
                 *
                 * _Available since v4.3._
                 */
                function verifyCallResult(
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal pure returns (bytes memory) {
                    if (success) {
                        return returndata;
                    } else {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
            }
            

            File 3 of 6: Forwarder
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity 0.8.10;
            import '@openzeppelin/contracts/token/ERC1155/IERC1155.sol';
            import '@openzeppelin/contracts/token/ERC721/IERC721.sol';
            import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol';
            import '@openzeppelin/contracts/token/ERC1155/utils/ERC1155Receiver.sol';
            import './ERC20Interface.sol';
            import './TransferHelper.sol';
            import './IForwarder.sol';
            /**
             * Contract that will forward any incoming Ether to the creator of the contract
             *
             */
            contract Forwarder is IERC721Receiver, ERC1155Receiver, IForwarder {
              // Address to which any funds sent to this contract will be forwarded
              address public parentAddress;
              bool public autoFlush721 = true;
              bool public autoFlush1155 = true;
              event ForwarderDeposited(address from, uint256 value, bytes data);
              /**
               * Initialize the contract, and sets the destination address to that of the creator
               */
              function init(
                address _parentAddress,
                bool _autoFlush721,
                bool _autoFlush1155
              ) external onlyUninitialized {
                parentAddress = _parentAddress;
                uint256 value = address(this).balance;
                // set whether we want to automatically flush erc721/erc1155 tokens or not
                autoFlush721 = _autoFlush721;
                autoFlush1155 = _autoFlush1155;
                if (value == 0) {
                  return;
                }
                (bool success, ) = parentAddress.call{ value: value }('');
                require(success, 'Flush failed');
                // NOTE: since we are forwarding on initialization,
                // we don't have the context of the original sender.
                // We still emit an event about the forwarding but set
                // the sender to the forwarder itself
                emit ForwarderDeposited(address(this), value, msg.data);
              }
              /**
               * Modifier that will execute internal code block only if the sender is the parent address
               */
              modifier onlyParent {
                require(msg.sender == parentAddress, 'Only Parent');
                _;
              }
              /**
               * Modifier that will execute internal code block only if the contract has not been initialized yet
               */
              modifier onlyUninitialized {
                require(parentAddress == address(0x0), 'Already initialized');
                _;
              }
              /**
               * Default function; Gets called when data is sent but does not match any other function
               */
              fallback() external payable {
                flush();
              }
              /**
               * Default function; Gets called when Ether is deposited with no data, and forwards it to the parent address
               */
              receive() external payable {
                flush();
              }
              /**
               * @inheritdoc IForwarder
               */
              function setAutoFlush721(bool autoFlush)
                external
                virtual
                override
                onlyParent
              {
                autoFlush721 = autoFlush;
              }
              /**
               * @inheritdoc IForwarder
               */
              function setAutoFlush1155(bool autoFlush)
                external
                virtual
                override
                onlyParent
              {
                autoFlush1155 = autoFlush;
              }
              /**
               * ERC721 standard callback function for when a ERC721 is transfered. The forwarder will send the nft
               * to the base wallet once the nft contract invokes this method after transfering the nft.
               *
               * @param _operator The address which called `safeTransferFrom` function
               * @param _from The address of the sender
               * @param _tokenId The token id of the nft
               * @param data Additional data with no specified format, sent in call to `_to`
               */
              function onERC721Received(
                address _operator,
                address _from,
                uint256 _tokenId,
                bytes memory data
              ) external virtual override returns (bytes4) {
                if (autoFlush721) {
                  IERC721 instance = IERC721(msg.sender);
                  require(
                    instance.supportsInterface(type(IERC721).interfaceId),
                    'The caller does not support the ERC721 interface'
                  );
                  // this won't work for ERC721 re-entrancy
                  instance.safeTransferFrom(address(this), parentAddress, _tokenId, data);
                }
                return this.onERC721Received.selector;
              }
              function callFromParent(
                address target,
                uint256 value,
                bytes calldata data
              ) external onlyParent returns (bytes memory) {
                (bool success, bytes memory returnedData) = target.call{ value: value }(
                  data
                );
                require(success, 'Parent call execution failed');
                return returnedData;
              }
              /**
               * @inheritdoc IERC1155Receiver
               */
              function onERC1155Received(
                address _operator,
                address _from,
                uint256 id,
                uint256 value,
                bytes calldata data
              ) external virtual override returns (bytes4) {
                IERC1155 instance = IERC1155(msg.sender);
                require(
                  instance.supportsInterface(type(IERC1155).interfaceId),
                  'The caller does not support the IERC1155 interface'
                );
                if (autoFlush1155) {
                  instance.safeTransferFrom(address(this), parentAddress, id, value, data);
                }
                return this.onERC1155Received.selector;
              }
              /**
               * @inheritdoc IERC1155Receiver
               */
              function onERC1155BatchReceived(
                address _operator,
                address _from,
                uint256[] calldata ids,
                uint256[] calldata values,
                bytes calldata data
              ) external virtual override returns (bytes4) {
                IERC1155 instance = IERC1155(msg.sender);
                require(
                  instance.supportsInterface(type(IERC1155).interfaceId),
                  'The caller does not support the IERC1155 interface'
                );
                if (autoFlush1155) {
                  instance.safeBatchTransferFrom(
                    address(this),
                    parentAddress,
                    ids,
                    values,
                    data
                  );
                }
                return this.onERC1155BatchReceived.selector;
              }
              /**
               * @inheritdoc IForwarder
               */
              function flushTokens(address tokenContractAddress)
                external
                virtual
                override
                onlyParent
              {
                ERC20Interface instance = ERC20Interface(tokenContractAddress);
                address forwarderAddress = address(this);
                uint256 forwarderBalance = instance.balanceOf(forwarderAddress);
                if (forwarderBalance == 0) {
                  return;
                }
                TransferHelper.safeTransfer(
                  tokenContractAddress,
                  parentAddress,
                  forwarderBalance
                );
              }
              /**
               * @inheritdoc IForwarder
               */
              function flushERC721Token(address tokenContractAddress, uint256 tokenId)
                external
                virtual
                override
                onlyParent
              {
                IERC721 instance = IERC721(tokenContractAddress);
                require(
                  instance.supportsInterface(type(IERC721).interfaceId),
                  'The tokenContractAddress does not support the ERC721 interface'
                );
                address ownerAddress = instance.ownerOf(tokenId);
                instance.transferFrom(ownerAddress, parentAddress, tokenId);
              }
              /**
               * @inheritdoc IForwarder
               */
              function flushERC1155Tokens(address tokenContractAddress, uint256 tokenId)
                external
                virtual
                override
                onlyParent
              {
                IERC1155 instance = IERC1155(tokenContractAddress);
                require(
                  instance.supportsInterface(type(IERC1155).interfaceId),
                  'The caller does not support the IERC1155 interface'
                );
                address forwarderAddress = address(this);
                uint256 forwarderBalance = instance.balanceOf(forwarderAddress, tokenId);
                instance.safeTransferFrom(
                  forwarderAddress,
                  parentAddress,
                  tokenId,
                  forwarderBalance,
                  ''
                );
              }
              /**
               * @inheritdoc IForwarder
               */
              function batchFlushERC1155Tokens(
                address tokenContractAddress,
                uint256[] calldata tokenIds
              ) external virtual override onlyParent {
                IERC1155 instance = IERC1155(tokenContractAddress);
                require(
                  instance.supportsInterface(type(IERC1155).interfaceId),
                  'The caller does not support the IERC1155 interface'
                );
                address forwarderAddress = address(this);
                uint256[] memory amounts = new uint256[](tokenIds.length);
                for (uint256 i = 0; i < tokenIds.length; i++) {
                  amounts[i] = instance.balanceOf(forwarderAddress, tokenIds[i]);
                }
                instance.safeBatchTransferFrom(
                  forwarderAddress,
                  parentAddress,
                  tokenIds,
                  amounts,
                  ''
                );
              }
              /**
               * Flush the entire balance of the contract to the parent address.
               */
              function flush() public {
                uint256 value = address(this).balance;
                if (value == 0) {
                  return;
                }
                (bool success, ) = parentAddress.call{ value: value }('');
                require(success, 'Flush failed');
                emit ForwarderDeposited(msg.sender, value, msg.data);
              }
              /**
               * @inheritdoc IERC165
               */
              function supportsInterface(bytes4 interfaceId)
                public
                virtual
                override(ERC1155Receiver, IERC165)
                view
                returns (bool)
              {
                return
                  interfaceId == type(IForwarder).interfaceId ||
                  super.supportsInterface(interfaceId);
              }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC1155/IERC1155.sol)
            pragma solidity ^0.8.0;
            import "../../utils/introspection/IERC165.sol";
            /**
             * @dev Required interface of an ERC1155 compliant contract, as defined in the
             * https://eips.ethereum.org/EIPS/eip-1155[EIP].
             *
             * _Available since v3.1._
             */
            interface IERC1155 is IERC165 {
                /**
                 * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
                 */
                event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
                /**
                 * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
                 * transfers.
                 */
                event TransferBatch(
                    address indexed operator,
                    address indexed from,
                    address indexed to,
                    uint256[] ids,
                    uint256[] values
                );
                /**
                 * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
                 * `approved`.
                 */
                event ApprovalForAll(address indexed account, address indexed operator, bool approved);
                /**
                 * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
                 *
                 * If an {URI} event was emitted for `id`, the standard
                 * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
                 * returned by {IERC1155MetadataURI-uri}.
                 */
                event URI(string value, uint256 indexed id);
                /**
                 * @dev Returns the amount of tokens of token type `id` owned by `account`.
                 *
                 * Requirements:
                 *
                 * - `account` cannot be the zero address.
                 */
                function balanceOf(address account, uint256 id) external view returns (uint256);
                /**
                 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
                 *
                 * Requirements:
                 *
                 * - `accounts` and `ids` must have the same length.
                 */
                function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
                    external
                    view
                    returns (uint256[] memory);
                /**
                 * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
                 *
                 * Emits an {ApprovalForAll} event.
                 *
                 * Requirements:
                 *
                 * - `operator` cannot be the caller.
                 */
                function setApprovalForAll(address operator, bool approved) external;
                /**
                 * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
                 *
                 * See {setApprovalForAll}.
                 */
                function isApprovedForAll(address account, address operator) external view returns (bool);
                /**
                 * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
                 *
                 * Emits a {TransferSingle} event.
                 *
                 * Requirements:
                 *
                 * - `to` cannot be the zero address.
                 * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
                 * - `from` must have a balance of tokens of type `id` of at least `amount`.
                 * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
                 * acceptance magic value.
                 */
                function safeTransferFrom(
                    address from,
                    address to,
                    uint256 id,
                    uint256 amount,
                    bytes calldata data
                ) external;
                /**
                 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
                 *
                 * Emits a {TransferBatch} event.
                 *
                 * Requirements:
                 *
                 * - `ids` and `amounts` must have the same length.
                 * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
                 * acceptance magic value.
                 */
                function safeBatchTransferFrom(
                    address from,
                    address to,
                    uint256[] calldata ids,
                    uint256[] calldata amounts,
                    bytes calldata data
                ) external;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)
            pragma solidity ^0.8.0;
            import "../../utils/introspection/IERC165.sol";
            /**
             * @dev Required interface of an ERC721 compliant contract.
             */
            interface IERC721 is IERC165 {
                /**
                 * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
                 */
                event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                /**
                 * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
                 */
                event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
                /**
                 * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
                 */
                event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
                /**
                 * @dev Returns the number of tokens in ``owner``'s account.
                 */
                function balanceOf(address owner) external view returns (uint256 balance);
                /**
                 * @dev Returns the owner of the `tokenId` token.
                 *
                 * Requirements:
                 *
                 * - `tokenId` must exist.
                 */
                function ownerOf(uint256 tokenId) external view returns (address owner);
                /**
                 * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
                 * are aware of the ERC721 protocol to prevent tokens from being forever locked.
                 *
                 * Requirements:
                 *
                 * - `from` cannot be the zero address.
                 * - `to` cannot be the zero address.
                 * - `tokenId` token must exist and be owned by `from`.
                 * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
                 * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                 *
                 * Emits a {Transfer} event.
                 */
                function safeTransferFrom(
                    address from,
                    address to,
                    uint256 tokenId
                ) external;
                /**
                 * @dev Transfers `tokenId` token from `from` to `to`.
                 *
                 * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
                 *
                 * Requirements:
                 *
                 * - `from` cannot be the zero address.
                 * - `to` cannot be the zero address.
                 * - `tokenId` token must be owned by `from`.
                 * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                 *
                 * Emits a {Transfer} event.
                 */
                function transferFrom(
                    address from,
                    address to,
                    uint256 tokenId
                ) external;
                /**
                 * @dev Gives permission to `to` to transfer `tokenId` token to another account.
                 * The approval is cleared when the token is transferred.
                 *
                 * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
                 *
                 * Requirements:
                 *
                 * - The caller must own the token or be an approved operator.
                 * - `tokenId` must exist.
                 *
                 * Emits an {Approval} event.
                 */
                function approve(address to, uint256 tokenId) external;
                /**
                 * @dev Returns the account approved for `tokenId` token.
                 *
                 * Requirements:
                 *
                 * - `tokenId` must exist.
                 */
                function getApproved(uint256 tokenId) external view returns (address operator);
                /**
                 * @dev Approve or remove `operator` as an operator for the caller.
                 * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
                 *
                 * Requirements:
                 *
                 * - The `operator` cannot be the caller.
                 *
                 * Emits an {ApprovalForAll} event.
                 */
                function setApprovalForAll(address operator, bool _approved) external;
                /**
                 * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
                 *
                 * See {setApprovalForAll}
                 */
                function isApprovedForAll(address owner, address operator) external view returns (bool);
                /**
                 * @dev Safely transfers `tokenId` token from `from` to `to`.
                 *
                 * Requirements:
                 *
                 * - `from` cannot be the zero address.
                 * - `to` cannot be the zero address.
                 * - `tokenId` token must exist and be owned by `from`.
                 * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                 * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                 *
                 * Emits a {Transfer} event.
                 */
                function safeTransferFrom(
                    address from,
                    address to,
                    uint256 tokenId,
                    bytes calldata data
                ) external;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)
            pragma solidity ^0.8.0;
            /**
             * @title ERC721 token receiver interface
             * @dev Interface for any contract that wants to support safeTransfers
             * from ERC721 asset contracts.
             */
            interface IERC721Receiver {
                /**
                 * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
                 * by `operator` from `from`, this function is called.
                 *
                 * It must return its Solidity selector to confirm the token transfer.
                 * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
                 *
                 * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
                 */
                function onERC721Received(
                    address operator,
                    address from,
                    uint256 tokenId,
                    bytes calldata data
                ) external returns (bytes4);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC1155/utils/ERC1155Receiver.sol)
            pragma solidity ^0.8.0;
            import "../IERC1155Receiver.sol";
            import "../../../utils/introspection/ERC165.sol";
            /**
             * @dev _Available since v3.1._
             */
            abstract contract ERC1155Receiver is ERC165, IERC1155Receiver {
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
                    return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
                }
            }
            // SPDX-License-Identifier: UNLICENSED
            pragma solidity 0.8.10;
            /**
             * Contract that exposes the needed erc20 token functions
             */
            abstract contract ERC20Interface {
              // Send _value amount of tokens to address _to
              function transfer(address _to, uint256 _value)
                public
                virtual
                returns (bool success);
              // Get the account balance of another account with address _owner
              function balanceOf(address _owner)
                public
                virtual
                view
                returns (uint256 balance);
            }
            // SPDX-License-Identifier: GPL-3.0-or-later
            // source: https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/TransferHelper.sol
            pragma solidity 0.8.10;
            import '@openzeppelin/contracts/utils/Address.sol';
            // helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
            library TransferHelper {
              function safeTransfer(
                address token,
                address to,
                uint256 value
              ) internal {
                // bytes4(keccak256(bytes('transfer(address,uint256)')));
                (bool success, bytes memory data) = token.call(
                  abi.encodeWithSelector(0xa9059cbb, to, value)
                );
                require(
                  success && (data.length == 0 || abi.decode(data, (bool))),
                  'TransferHelper::safeTransfer: transfer failed'
                );
              }
              function safeTransferFrom(
                address token,
                address from,
                address to,
                uint256 value
              ) internal {
                // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
                (bool success, bytes memory returndata) = token.call(
                  abi.encodeWithSelector(0x23b872dd, from, to, value)
                );
                Address.verifyCallResult(
                  success,
                  returndata,
                  'TransferHelper::transferFrom: transferFrom failed'
                );
              }
            }
            pragma solidity ^0.8.0;
            import '@openzeppelin/contracts/utils/introspection/IERC165.sol';
            interface IForwarder is IERC165 {
              /**
               * Sets the autoflush721 parameter.
               *
               * @param autoFlush whether to autoflush erc721 tokens
               */
              function setAutoFlush721(bool autoFlush) external;
              /**
               * Sets the autoflush1155 parameter.
               *
               * @param autoFlush whether to autoflush erc1155 tokens
               */
              function setAutoFlush1155(bool autoFlush) external;
              /**
               * Execute a token transfer of the full balance from the forwarder token to the parent address
               *
               * @param tokenContractAddress the address of the erc20 token contract
               */
              function flushTokens(address tokenContractAddress) external;
              /**
               * Execute a nft transfer from the forwarder to the parent address
               *
               * @param tokenContractAddress the address of the ERC721 NFT contract
               * @param tokenId The token id of the nft
               */
              function flushERC721Token(address tokenContractAddress, uint256 tokenId)
                external;
              /**
               * Execute a nft transfer from the forwarder to the parent address.
               *
               * @param tokenContractAddress the address of the ERC1155 NFT contract
               * @param tokenId The token id of the nft
               */
              function flushERC1155Tokens(address tokenContractAddress, uint256 tokenId)
                external;
              /**
               * Execute a batch nft transfer from the forwarder to the parent address.
               *
               * @param tokenContractAddress the address of the ERC1155 NFT contract
               * @param tokenIds The token ids of the nfts
               */
              function batchFlushERC1155Tokens(
                address tokenContractAddress,
                uint256[] calldata tokenIds
              ) external;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Interface of the ERC165 standard, as defined in the
             * https://eips.ethereum.org/EIPS/eip-165[EIP].
             *
             * Implementers can declare support of contract interfaces, which can then be
             * queried by others ({ERC165Checker}).
             *
             * For an implementation, see {ERC165}.
             */
            interface IERC165 {
                /**
                 * @dev Returns true if this contract implements the interface defined by
                 * `interfaceId`. See the corresponding
                 * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
                 * to learn more about how these ids are created.
                 *
                 * This function call must use less than 30 000 gas.
                 */
                function supportsInterface(bytes4 interfaceId) external view returns (bool);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC1155/IERC1155Receiver.sol)
            pragma solidity ^0.8.0;
            import "../../utils/introspection/IERC165.sol";
            /**
             * @dev _Available since v3.1._
             */
            interface IERC1155Receiver is IERC165 {
                /**
                    @dev Handles the receipt of a single ERC1155 token type. This function is
                    called at the end of a `safeTransferFrom` after the balance has been updated.
                    To accept the transfer, this must return
                    `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
                    (i.e. 0xf23a6e61, or its own function selector).
                    @param operator The address which initiated the transfer (i.e. msg.sender)
                    @param from The address which previously owned the token
                    @param id The ID of the token being transferred
                    @param value The amount of tokens being transferred
                    @param data Additional data with no specified format
                    @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
                */
                function onERC1155Received(
                    address operator,
                    address from,
                    uint256 id,
                    uint256 value,
                    bytes calldata data
                ) external returns (bytes4);
                /**
                    @dev Handles the receipt of a multiple ERC1155 token types. This function
                    is called at the end of a `safeBatchTransferFrom` after the balances have
                    been updated. To accept the transfer(s), this must return
                    `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
                    (i.e. 0xbc197c81, or its own function selector).
                    @param operator The address which initiated the batch transfer (i.e. msg.sender)
                    @param from The address which previously owned the token
                    @param ids An array containing ids of each token being transferred (order and length must match values array)
                    @param values An array containing amounts of each token being transferred (order and length must match ids array)
                    @param data Additional data with no specified format
                    @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
                */
                function onERC1155BatchReceived(
                    address operator,
                    address from,
                    uint256[] calldata ids,
                    uint256[] calldata values,
                    bytes calldata data
                ) external returns (bytes4);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
            pragma solidity ^0.8.0;
            import "./IERC165.sol";
            /**
             * @dev Implementation of the {IERC165} interface.
             *
             * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
             * for the additional interface id that will be supported. For example:
             *
             * ```solidity
             * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
             *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
             * }
             * ```
             *
             * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
             */
            abstract contract ERC165 is IERC165 {
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                    return interfaceId == type(IERC165).interfaceId;
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/Address.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Collection of functions related to the address type
             */
            library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // This method relies on extcodesize, which returns 0 for contracts in
                    // construction, since the code is only stored at the end of the
                    // constructor execution.
                    uint256 size;
                    assembly {
                        size := extcodesize(account)
                    }
                    return size > 0;
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
                    (bool success, ) = recipient.call{value: amount}("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain `call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCall(target, data, "Address: low-level call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                 * `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, 0, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but also transferring `value` wei to `target`.
                 *
                 * Requirements:
                 *
                 * - the calling contract must have an ETH balance of at least `value`.
                 * - the called Solidity function must be `payable`.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(address(this).balance >= value, "Address: insufficient balance for call");
                    require(isContract(target), "Address: call to non-contract");
                    (bool success, bytes memory returndata) = target.call{value: value}(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                    return functionStaticCall(target, data, "Address: low-level static call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    require(isContract(target), "Address: static call to non-contract");
                    (bool success, bytes memory returndata) = target.staticcall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(isContract(target), "Address: delegate call to non-contract");
                    (bool success, bytes memory returndata) = target.delegatecall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                 * revert reason using the provided one.
                 *
                 * _Available since v4.3._
                 */
                function verifyCallResult(
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal pure returns (bytes memory) {
                    if (success) {
                        return returndata;
                    } else {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
            }
            

            File 4 of 6: WalletSimple
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity 0.8.10;
            import './TransferHelper.sol';
            import './ERC20Interface.sol';
            import './IForwarder.sol';
            /** ERC721, ERC1155 imports */
            import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol';
            import '@openzeppelin/contracts/token/ERC1155/utils/ERC1155Receiver.sol';
            /**
             *
             * WalletSimple
             * ============
             *
             * Basic multi-signer wallet designed for use in a co-signing environment where 2 signatures are required to move funds.
             * Typically used in a 2-of-3 signing configuration. Uses ecrecover to allow for 2 signatures in a single transaction.
             *
             * The first signature is created on the operation hash (see Data Formats) and passed to sendMultiSig/sendMultiSigToken
             * The signer is determined by verifyMultiSig().
             *
             * The second signature is created by the submitter of the transaction and determined by msg.signer.
             *
             * Data Formats
             * ============
             *
             * The signature is created with ethereumjs-util.ecsign(operationHash).
             * Like the eth_sign RPC call, it packs the values as a 65-byte array of [r, s, v].
             * Unlike eth_sign, the message is not prefixed.
             *
             * The operationHash the result of keccak256(prefix, toAddress, value, data, expireTime).
             * For ether transactions, `prefix` is "ETHER".
             * For token transaction, `prefix` is "ERC20" and `data` is the tokenContractAddress.
             *
             *
             */
            contract WalletSimple is IERC721Receiver, ERC1155Receiver {
              // Events
              event Deposited(address from, uint256 value, bytes data);
              event SafeModeActivated(address msgSender);
              event Transacted(
                address msgSender, // Address of the sender of the message initiating the transaction
                address otherSigner, // Address of the signer (second signature) used to initiate the transaction
                bytes32 operation, // Operation hash (see Data Formats)
                address toAddress, // The address the transaction was sent to
                uint256 value, // Amount of Wei sent to the address
                bytes data // Data sent when invoking the transaction
              );
              event BatchTransfer(address sender, address recipient, uint256 value);
              // this event shows the other signer and the operation hash that they signed
              // specific batch transfer events are emitted in Batcher
              event BatchTransacted(
                address msgSender, // Address of the sender of the message initiating the transaction
                address otherSigner, // Address of the signer (second signature) used to initiate the transaction
                bytes32 operation // Operation hash (see Data Formats)
              );
              // Public fields
              mapping(address => bool) public signers; // The addresses that can co-sign transactions on the wallet
              bool public safeMode = false; // When active, wallet may only send to signer addresses
              bool public initialized = false; // True if the contract has been initialized
              // Internal fields
              uint256 private constant MAX_SEQUENCE_ID_INCREASE = 10000;
              uint256 constant SEQUENCE_ID_WINDOW_SIZE = 10;
              uint256[SEQUENCE_ID_WINDOW_SIZE] recentSequenceIds;
              /**
               * Set up a simple multi-sig wallet by specifying the signers allowed to be used on this wallet.
               * 2 signers will be required to send a transaction from this wallet.
               * Note: The sender is NOT automatically added to the list of signers.
               * Signers CANNOT be changed once they are set
               *
               * @param allowedSigners An array of signers on the wallet
               */
              function init(address[] calldata allowedSigners) external onlyUninitialized {
                require(allowedSigners.length == 3, 'Invalid number of signers');
                for (uint8 i = 0; i < allowedSigners.length; i++) {
                  require(allowedSigners[i] != address(0), 'Invalid signer');
                  signers[allowedSigners[i]] = true;
                }
                initialized = true;
              }
              /**
               * Get the network identifier that signers must sign over
               * This provides protection signatures being replayed on other chains
               * This must be a virtual function because chain-specific contracts will need
               *    to override with their own network ids. It also can't be a field
               *    to allow this contract to be used by proxy with delegatecall, which will
               *    not pick up on state variables
               */
              function getNetworkId() internal virtual pure returns (string memory) {
                return 'ETHER';
              }
              /**
               * Get the network identifier that signers must sign over for token transfers
               * This provides protection signatures being replayed on other chains
               * This must be a virtual function because chain-specific contracts will need
               *    to override with their own network ids. It also can't be a field
               *    to allow this contract to be used by proxy with delegatecall, which will
               *    not pick up on state variables
               */
              function getTokenNetworkId() internal virtual pure returns (string memory) {
                return 'ERC20';
              }
              /**
               * Get the network identifier that signers must sign over for batch transfers
               * This provides protection signatures being replayed on other chains
               * This must be a virtual function because chain-specific contracts will need
               *    to override with their own network ids. It also can't be a field
               *    to allow this contract to be used by proxy with delegatecall, which will
               *    not pick up on state variables
               */
              function getBatchNetworkId() internal virtual pure returns (string memory) {
                return 'ETHER-Batch';
              }
              /**
               * Determine if an address is a signer on this wallet
               * @param signer address to check
               * returns boolean indicating whether address is signer or not
               */
              function isSigner(address signer) public view returns (bool) {
                return signers[signer];
              }
              /**
               * Modifier that will execute internal code block only if the sender is an authorized signer on this wallet
               */
              modifier onlySigner {
                require(isSigner(msg.sender), 'Non-signer in onlySigner method');
                _;
              }
              /**
               * Modifier that will execute internal code block only if the contract has not been initialized yet
               */
              modifier onlyUninitialized {
                require(!initialized, 'Contract already initialized');
                _;
              }
              /**
               * Gets called when a transaction is received with data that does not match any other method
               */
              fallback() external payable {
                if (msg.value > 0) {
                  // Fire deposited event if we are receiving funds
                  emit Deposited(msg.sender, msg.value, msg.data);
                }
              }
              /**
               * Gets called when a transaction is received with ether and no data
               */
              receive() external payable {
                if (msg.value > 0) {
                  // Fire deposited event if we are receiving funds
                  // message data is always empty for receive. If there is data it is sent to fallback function.
                  emit Deposited(msg.sender, msg.value, '');
                }
              }
              /**
               * Execute a multi-signature transaction from this wallet using 2 signers: one from msg.sender and the other from ecrecover.
               * Sequence IDs are numbers starting from 1. They are used to prevent replay attacks and may not be repeated.
               *
               * @param toAddress the destination address to send an outgoing transaction
               * @param value the amount in Wei to be sent
               * @param data the data to send to the toAddress when invoking the transaction
               * @param expireTime the number of seconds since 1970 for which this transaction is valid
               * @param sequenceId the unique sequence id obtainable from getNextSequenceId
               * @param signature see Data Formats
               */
              function sendMultiSig(
                address toAddress,
                uint256 value,
                bytes calldata data,
                uint256 expireTime,
                uint256 sequenceId,
                bytes calldata signature
              ) external onlySigner {
                // Verify the other signer
                bytes32 operationHash = keccak256(
                  abi.encodePacked(
                    getNetworkId(),
                    toAddress,
                    value,
                    data,
                    expireTime,
                    sequenceId
                  )
                );
                address otherSigner = verifyMultiSig(
                  toAddress,
                  operationHash,
                  signature,
                  expireTime,
                  sequenceId
                );
                // Success, send the transaction
                (bool success, ) = toAddress.call{ value: value }(data);
                require(success, 'Call execution failed');
                emit Transacted(
                  msg.sender,
                  otherSigner,
                  operationHash,
                  toAddress,
                  value,
                  data
                );
              }
              /**
               * Execute a batched multi-signature transaction from this wallet using 2 signers: one from msg.sender and the other from ecrecover.
               * Sequence IDs are numbers starting from 1. They are used to prevent replay attacks and may not be repeated.
               * The recipients and values to send are encoded in two arrays, where for index i, recipients[i] will be sent values[i].
               *
               * @param recipients The list of recipients to send to
               * @param values The list of values to send to
               * @param expireTime the number of seconds since 1970 for which this transaction is valid
               * @param sequenceId the unique sequence id obtainable from getNextSequenceId
               * @param signature see Data Formats
               */
              function sendMultiSigBatch(
                address[] calldata recipients,
                uint256[] calldata values,
                uint256 expireTime,
                uint256 sequenceId,
                bytes calldata signature
              ) external onlySigner {
                require(recipients.length != 0, 'Not enough recipients');
                require(
                  recipients.length == values.length,
                  'Unequal recipients and values'
                );
                require(recipients.length < 256, 'Too many recipients, max 255');
                // Verify the other signer
                bytes32 operationHash = keccak256(
                  abi.encodePacked(
                    getBatchNetworkId(),
                    recipients,
                    values,
                    expireTime,
                    sequenceId
                  )
                );
                // the first parameter (toAddress) is used to ensure transactions in safe mode only go to a signer
                // if in safe mode, we should use normal sendMultiSig to recover, so this check will always fail if in safe mode
                require(!safeMode, 'Batch in safe mode');
                address otherSigner = verifyMultiSig(
                  address(0x0),
                  operationHash,
                  signature,
                  expireTime,
                  sequenceId
                );
                batchTransfer(recipients, values);
                emit BatchTransacted(msg.sender, otherSigner, operationHash);
              }
              /**
               * Transfer funds in a batch to each of recipients
               * @param recipients The list of recipients to send to
               * @param values The list of values to send to recipients.
               *  The recipient with index i in recipients array will be sent values[i].
               *  Thus, recipients and values must be the same length
               */
              function batchTransfer(
                address[] calldata recipients,
                uint256[] calldata values
              ) internal {
                for (uint256 i = 0; i < recipients.length; i++) {
                  require(address(this).balance >= values[i], 'Insufficient funds');
                  (bool success, ) = recipients[i].call{ value: values[i] }('');
                  require(success, 'Call failed');
                  emit BatchTransfer(msg.sender, recipients[i], values[i]);
                }
              }
              /**
               * Execute a multi-signature token transfer from this wallet using 2 signers: one from msg.sender and the other from ecrecover.
               * Sequence IDs are numbers starting from 1. They are used to prevent replay attacks and may not be repeated.
               *
               * @param toAddress the destination address to send an outgoing transaction
               * @param value the amount in tokens to be sent
               * @param tokenContractAddress the address of the erc20 token contract
               * @param expireTime the number of seconds since 1970 for which this transaction is valid
               * @param sequenceId the unique sequence id obtainable from getNextSequenceId
               * @param signature see Data Formats
               */
              function sendMultiSigToken(
                address toAddress,
                uint256 value,
                address tokenContractAddress,
                uint256 expireTime,
                uint256 sequenceId,
                bytes calldata signature
              ) external onlySigner {
                // Verify the other signer
                bytes32 operationHash = keccak256(
                  abi.encodePacked(
                    getTokenNetworkId(),
                    toAddress,
                    value,
                    tokenContractAddress,
                    expireTime,
                    sequenceId
                  )
                );
                verifyMultiSig(toAddress, operationHash, signature, expireTime, sequenceId);
                TransferHelper.safeTransfer(tokenContractAddress, toAddress, value);
              }
              /**
               * Execute a token flush from one of the forwarder addresses. This transfer needs only a single signature and can be done by any signer
               *
               * @param forwarderAddress the address of the forwarder address to flush the tokens from
               * @param tokenContractAddress the address of the erc20 token contract
               */
              function flushForwarderTokens(
                address payable forwarderAddress,
                address tokenContractAddress
              ) external onlySigner {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.flushTokens(tokenContractAddress);
              }
              /**
               * Execute a ERC721 token flush from one of the forwarder addresses. This transfer needs only a single signature and can be done by any signer
               *
               * @param forwarderAddress the address of the forwarder address to flush the tokens from
               * @param tokenContractAddress the address of the erc20 token contract
               */
              function flushERC721ForwarderTokens(
                address payable forwarderAddress,
                address tokenContractAddress,
                uint256 tokenId
              ) external onlySigner {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.flushERC721Token(tokenContractAddress, tokenId);
              }
              /**
               * Execute a ERC1155 batch token flush from one of the forwarder addresses.
               * This transfer needs only a single signature and can be done by any signer.
               *
               * @param forwarderAddress the address of the forwarder address to flush the tokens from
               * @param tokenContractAddress the address of the erc1155 token contract
               */
              function batchFlushERC1155ForwarderTokens(
                address payable forwarderAddress,
                address tokenContractAddress,
                uint256[] calldata tokenIds
              ) external onlySigner {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.batchFlushERC1155Tokens(tokenContractAddress, tokenIds);
              }
              /**
               * Execute a ERC1155 token flush from one of the forwarder addresses.
               * This transfer needs only a single signature and can be done by any signer.
               *
               * @param forwarderAddress the address of the forwarder address to flush the tokens from
               * @param tokenContractAddress the address of the erc1155 token contract
               * @param tokenId the token id associated with the ERC1155
               */
              function flushERC1155ForwarderTokens(
                address payable forwarderAddress,
                address tokenContractAddress,
                uint256 tokenId
              ) external onlySigner {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.flushERC1155Tokens(tokenContractAddress, tokenId);
              }
              /**
               * Sets the autoflush 721 parameter on the forwarder.
               *
               * @param forwarderAddress the address of the forwarder to toggle.
               * @param autoFlush whether to autoflush erc721 tokens
               */
              function setAutoFlush721(address forwarderAddress, bool autoFlush)
                external
                onlySigner
              {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.setAutoFlush721(autoFlush);
              }
              /**
               * Sets the autoflush 721 parameter on the forwarder.
               *
               * @param forwarderAddress the address of the forwarder to toggle.
               * @param autoFlush whether to autoflush erc1155 tokens
               */
              function setAutoFlush1155(address forwarderAddress, bool autoFlush)
                external
                onlySigner
              {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.setAutoFlush1155(autoFlush);
              }
              /**
               * Do common multisig verification for both eth sends and erc20token transfers
               *
               * @param toAddress the destination address to send an outgoing transaction
               * @param operationHash see Data Formats
               * @param signature see Data Formats
               * @param expireTime the number of seconds since 1970 for which this transaction is valid
               * @param sequenceId the unique sequence id obtainable from getNextSequenceId
               * returns address that has created the signature
               */
              function verifyMultiSig(
                address toAddress,
                bytes32 operationHash,
                bytes calldata signature,
                uint256 expireTime,
                uint256 sequenceId
              ) private returns (address) {
                address otherSigner = recoverAddressFromSignature(operationHash, signature);
                // Verify if we are in safe mode. In safe mode, the wallet can only send to signers
                require(!safeMode || isSigner(toAddress), 'External transfer in safe mode');
                // Verify that the transaction has not expired
                require(expireTime >= block.timestamp, 'Transaction expired');
                // Try to insert the sequence ID. Will revert if the sequence id was invalid
                tryInsertSequenceId(sequenceId);
                require(isSigner(otherSigner), 'Invalid signer');
                require(otherSigner != msg.sender, 'Signers cannot be equal');
                return otherSigner;
              }
              /**
               * ERC721 standard callback function for when a ERC721 is transfered.
               *
               * @param _operator The address of the nft contract
               * @param _from The address of the sender
               * @param _tokenId The token id of the nft
               * @param _data Additional data with no specified format, sent in call to `_to`
               */
              function onERC721Received(
                address _operator,
                address _from,
                uint256 _tokenId,
                bytes memory _data
              ) external virtual override returns (bytes4) {
                return this.onERC721Received.selector;
              }
              /**
               * @inheritdoc IERC1155Receiver
               */
              function onERC1155Received(
                address _operator,
                address _from,
                uint256 id,
                uint256 value,
                bytes calldata data
              ) external virtual override returns (bytes4) {
                return this.onERC1155Received.selector;
              }
              /**
               * @inheritdoc IERC1155Receiver
               */
              function onERC1155BatchReceived(
                address _operator,
                address _from,
                uint256[] calldata ids,
                uint256[] calldata values,
                bytes calldata data
              ) external virtual override returns (bytes4) {
                return this.onERC1155BatchReceived.selector;
              }
              /**
               * Irrevocably puts contract into safe mode. When in this mode, transactions may only be sent to signing addresses.
               */
              function activateSafeMode() external onlySigner {
                safeMode = true;
                emit SafeModeActivated(msg.sender);
              }
              /**
               * Gets signer's address using ecrecover
               * @param operationHash see Data Formats
               * @param signature see Data Formats
               * returns address recovered from the signature
               */
              function recoverAddressFromSignature(
                bytes32 operationHash,
                bytes memory signature
              ) private pure returns (address) {
                require(signature.length == 65, 'Invalid signature - wrong length');
                // We need to unpack the signature, which is given as an array of 65 bytes (like eth.sign)
                bytes32 r;
                bytes32 s;
                uint8 v;
                // solhint-disable-next-line
                assembly {
                  r := mload(add(signature, 32))
                  s := mload(add(signature, 64))
                  v := and(mload(add(signature, 65)), 255)
                }
                if (v < 27) {
                  v += 27; // Ethereum versions are 27 or 28 as opposed to 0 or 1 which is submitted by some signing libs
                }
                // protect against signature malleability
                // S value must be in the lower half orader
                // reference: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/051d340171a93a3d401aaaea46b4b62fa81e5d7c/contracts/cryptography/ECDSA.sol#L53
                require(
                  uint256(s) <=
                    0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
                  "ECDSA: invalid signature 's' value"
                );
                // note that this returns 0 if the signature is invalid
                // Since 0x0 can never be a signer, when the recovered signer address
                // is checked against our signer list, that 0x0 will cause an invalid signer failure
                return ecrecover(operationHash, v, r, s);
              }
              /**
               * Verify that the sequence id has not been used before and inserts it. Throws if the sequence ID was not accepted.
               * We collect a window of up to 10 recent sequence ids, and allow any sequence id that is not in the window and
               * greater than the minimum element in the window.
               * @param sequenceId to insert into array of stored ids
               */
              function tryInsertSequenceId(uint256 sequenceId) private onlySigner {
                // Keep a pointer to the lowest value element in the window
                uint256 lowestValueIndex = 0;
                // fetch recentSequenceIds into memory for function context to avoid unnecessary sloads
                  uint256[SEQUENCE_ID_WINDOW_SIZE] memory _recentSequenceIds
                 = recentSequenceIds;
                for (uint256 i = 0; i < SEQUENCE_ID_WINDOW_SIZE; i++) {
                  require(_recentSequenceIds[i] != sequenceId, 'Sequence ID already used');
                  if (_recentSequenceIds[i] < _recentSequenceIds[lowestValueIndex]) {
                    lowestValueIndex = i;
                  }
                }
                // The sequence ID being used is lower than the lowest value in the window
                // so we cannot accept it as it may have been used before
                require(
                  sequenceId > _recentSequenceIds[lowestValueIndex],
                  'Sequence ID below window'
                );
                // Block sequence IDs which are much higher than the lowest value
                // This prevents people blocking the contract by using very large sequence IDs quickly
                require(
                  sequenceId <=
                    (_recentSequenceIds[lowestValueIndex] + MAX_SEQUENCE_ID_INCREASE),
                  'Sequence ID above maximum'
                );
                recentSequenceIds[lowestValueIndex] = sequenceId;
              }
              /**
               * Gets the next available sequence ID for signing when using executeAndConfirm
               * returns the sequenceId one higher than the highest currently stored
               */
              function getNextSequenceId() external view returns (uint256) {
                uint256 highestSequenceId = 0;
                for (uint256 i = 0; i < SEQUENCE_ID_WINDOW_SIZE; i++) {
                  if (recentSequenceIds[i] > highestSequenceId) {
                    highestSequenceId = recentSequenceIds[i];
                  }
                }
                return highestSequenceId + 1;
              }
            }
            // SPDX-License-Identifier: GPL-3.0-or-later
            // source: https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/TransferHelper.sol
            pragma solidity 0.8.10;
            import '@openzeppelin/contracts/utils/Address.sol';
            // helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
            library TransferHelper {
              function safeTransfer(
                address token,
                address to,
                uint256 value
              ) internal {
                // bytes4(keccak256(bytes('transfer(address,uint256)')));
                (bool success, bytes memory data) = token.call(
                  abi.encodeWithSelector(0xa9059cbb, to, value)
                );
                require(
                  success && (data.length == 0 || abi.decode(data, (bool))),
                  'TransferHelper::safeTransfer: transfer failed'
                );
              }
              function safeTransferFrom(
                address token,
                address from,
                address to,
                uint256 value
              ) internal {
                // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
                (bool success, bytes memory returndata) = token.call(
                  abi.encodeWithSelector(0x23b872dd, from, to, value)
                );
                Address.verifyCallResult(
                  success,
                  returndata,
                  'TransferHelper::transferFrom: transferFrom failed'
                );
              }
            }
            // SPDX-License-Identifier: UNLICENSED
            pragma solidity 0.8.10;
            /**
             * Contract that exposes the needed erc20 token functions
             */
            abstract contract ERC20Interface {
              // Send _value amount of tokens to address _to
              function transfer(address _to, uint256 _value)
                public
                virtual
                returns (bool success);
              // Get the account balance of another account with address _owner
              function balanceOf(address _owner)
                public
                virtual
                view
                returns (uint256 balance);
            }
            pragma solidity ^0.8.0;
            import '@openzeppelin/contracts/utils/introspection/IERC165.sol';
            interface IForwarder is IERC165 {
              /**
               * Sets the autoflush721 parameter.
               *
               * @param autoFlush whether to autoflush erc721 tokens
               */
              function setAutoFlush721(bool autoFlush) external;
              /**
               * Sets the autoflush1155 parameter.
               *
               * @param autoFlush whether to autoflush erc1155 tokens
               */
              function setAutoFlush1155(bool autoFlush) external;
              /**
               * Execute a token transfer of the full balance from the forwarder token to the parent address
               *
               * @param tokenContractAddress the address of the erc20 token contract
               */
              function flushTokens(address tokenContractAddress) external;
              /**
               * Execute a nft transfer from the forwarder to the parent address
               *
               * @param tokenContractAddress the address of the ERC721 NFT contract
               * @param tokenId The token id of the nft
               */
              function flushERC721Token(address tokenContractAddress, uint256 tokenId)
                external;
              /**
               * Execute a nft transfer from the forwarder to the parent address.
               *
               * @param tokenContractAddress the address of the ERC1155 NFT contract
               * @param tokenId The token id of the nft
               */
              function flushERC1155Tokens(address tokenContractAddress, uint256 tokenId)
                external;
              /**
               * Execute a batch nft transfer from the forwarder to the parent address.
               *
               * @param tokenContractAddress the address of the ERC1155 NFT contract
               * @param tokenIds The token ids of the nfts
               */
              function batchFlushERC1155Tokens(
                address tokenContractAddress,
                uint256[] calldata tokenIds
              ) external;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)
            pragma solidity ^0.8.0;
            /**
             * @title ERC721 token receiver interface
             * @dev Interface for any contract that wants to support safeTransfers
             * from ERC721 asset contracts.
             */
            interface IERC721Receiver {
                /**
                 * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
                 * by `operator` from `from`, this function is called.
                 *
                 * It must return its Solidity selector to confirm the token transfer.
                 * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
                 *
                 * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
                 */
                function onERC721Received(
                    address operator,
                    address from,
                    uint256 tokenId,
                    bytes calldata data
                ) external returns (bytes4);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC1155/utils/ERC1155Receiver.sol)
            pragma solidity ^0.8.0;
            import "../IERC1155Receiver.sol";
            import "../../../utils/introspection/ERC165.sol";
            /**
             * @dev _Available since v3.1._
             */
            abstract contract ERC1155Receiver is ERC165, IERC1155Receiver {
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
                    return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/Address.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Collection of functions related to the address type
             */
            library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // This method relies on extcodesize, which returns 0 for contracts in
                    // construction, since the code is only stored at the end of the
                    // constructor execution.
                    uint256 size;
                    assembly {
                        size := extcodesize(account)
                    }
                    return size > 0;
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
                    (bool success, ) = recipient.call{value: amount}("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain `call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCall(target, data, "Address: low-level call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                 * `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, 0, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but also transferring `value` wei to `target`.
                 *
                 * Requirements:
                 *
                 * - the calling contract must have an ETH balance of at least `value`.
                 * - the called Solidity function must be `payable`.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(address(this).balance >= value, "Address: insufficient balance for call");
                    require(isContract(target), "Address: call to non-contract");
                    (bool success, bytes memory returndata) = target.call{value: value}(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                    return functionStaticCall(target, data, "Address: low-level static call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    require(isContract(target), "Address: static call to non-contract");
                    (bool success, bytes memory returndata) = target.staticcall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(isContract(target), "Address: delegate call to non-contract");
                    (bool success, bytes memory returndata) = target.delegatecall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                 * revert reason using the provided one.
                 *
                 * _Available since v4.3._
                 */
                function verifyCallResult(
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal pure returns (bytes memory) {
                    if (success) {
                        return returndata;
                    } else {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Interface of the ERC165 standard, as defined in the
             * https://eips.ethereum.org/EIPS/eip-165[EIP].
             *
             * Implementers can declare support of contract interfaces, which can then be
             * queried by others ({ERC165Checker}).
             *
             * For an implementation, see {ERC165}.
             */
            interface IERC165 {
                /**
                 * @dev Returns true if this contract implements the interface defined by
                 * `interfaceId`. See the corresponding
                 * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
                 * to learn more about how these ids are created.
                 *
                 * This function call must use less than 30 000 gas.
                 */
                function supportsInterface(bytes4 interfaceId) external view returns (bool);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC1155/IERC1155Receiver.sol)
            pragma solidity ^0.8.0;
            import "../../utils/introspection/IERC165.sol";
            /**
             * @dev _Available since v3.1._
             */
            interface IERC1155Receiver is IERC165 {
                /**
                    @dev Handles the receipt of a single ERC1155 token type. This function is
                    called at the end of a `safeTransferFrom` after the balance has been updated.
                    To accept the transfer, this must return
                    `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
                    (i.e. 0xf23a6e61, or its own function selector).
                    @param operator The address which initiated the transfer (i.e. msg.sender)
                    @param from The address which previously owned the token
                    @param id The ID of the token being transferred
                    @param value The amount of tokens being transferred
                    @param data Additional data with no specified format
                    @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
                */
                function onERC1155Received(
                    address operator,
                    address from,
                    uint256 id,
                    uint256 value,
                    bytes calldata data
                ) external returns (bytes4);
                /**
                    @dev Handles the receipt of a multiple ERC1155 token types. This function
                    is called at the end of a `safeBatchTransferFrom` after the balances have
                    been updated. To accept the transfer(s), this must return
                    `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
                    (i.e. 0xbc197c81, or its own function selector).
                    @param operator The address which initiated the batch transfer (i.e. msg.sender)
                    @param from The address which previously owned the token
                    @param ids An array containing ids of each token being transferred (order and length must match values array)
                    @param values An array containing amounts of each token being transferred (order and length must match ids array)
                    @param data Additional data with no specified format
                    @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
                */
                function onERC1155BatchReceived(
                    address operator,
                    address from,
                    uint256[] calldata ids,
                    uint256[] calldata values,
                    bytes calldata data
                ) external returns (bytes4);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
            pragma solidity ^0.8.0;
            import "./IERC165.sol";
            /**
             * @dev Implementation of the {IERC165} interface.
             *
             * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
             * for the additional interface id that will be supported. For example:
             *
             * ```solidity
             * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
             *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
             * }
             * ```
             *
             * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
             */
            abstract contract ERC165 is IERC165 {
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                    return interfaceId == type(IERC165).interfaceId;
                }
            }
            

            File 5 of 6: CoinbaseSmartWallet
            // SPDX-License-Identifier: MIT
            pragma solidity 0.8.23;
            import {IAccount} from "account-abstraction/interfaces/IAccount.sol";
            import {UserOperation, UserOperationLib} from "account-abstraction/interfaces/UserOperation.sol";
            import {Receiver} from "solady/accounts/Receiver.sol";
            import {SignatureCheckerLib} from "solady/utils/SignatureCheckerLib.sol";
            import {UUPSUpgradeable} from "solady/utils/UUPSUpgradeable.sol";
            import {WebAuthn} from "webauthn-sol/WebAuthn.sol";
            import {ERC1271} from "./ERC1271.sol";
            import {MultiOwnable} from "./MultiOwnable.sol";
            /// @title Coinbase Smart Wallet
            ///
            /// @notice ERC-4337-compatible smart account, based on Solady's ERC4337 account implementation
            ///         with inspiration from Alchemy's LightAccount and Daimo's DaimoAccount. Verified by z0r0z.eth from (⌘) NANI.eth
            ///
            /// @author Coinbase (https://github.com/coinbase/smart-wallet)
            /// @author Solady (https://github.com/vectorized/solady/blob/main/src/accounts/ERC4337.sol)
            contract CoinbaseSmartWallet is ERC1271, IAccount, MultiOwnable, UUPSUpgradeable, Receiver {
                /// @notice A wrapper struct used for signature validation so that callers
                ///         can identify the owner that signed.
                struct SignatureWrapper {
                    /// @dev The index of the owner that signed, see `MultiOwnable.ownerAtIndex`
                    uint256 ownerIndex;
                    /// @dev If `MultiOwnable.ownerAtIndex` is an Ethereum address, this should be `abi.encodePacked(r, s, v)`
                    ///      If `MultiOwnable.ownerAtIndex` is a public key, this should be `abi.encode(WebAuthnAuth)`.
                    bytes signatureData;
                }
                /// @notice Represents a call to make.
                struct Call {
                    /// @dev The address to call.
                    address target;
                    /// @dev The value to send when making the call.
                    uint256 value;
                    /// @dev The data of the call.
                    bytes data;
                }
                /// @notice Reserved nonce key (upper 192 bits of `UserOperation.nonce`) for cross-chain replayable
                ///         transactions.
                ///
                /// @dev MUST BE the `UserOperation.nonce` key when `UserOperation.calldata` is calling
                ///      `executeWithoutChainIdValidation`and MUST NOT BE `UserOperation.nonce` key when `UserOperation.calldata` is
                ///      NOT calling `executeWithoutChainIdValidation`.
                ///
                /// @dev Helps enforce sequential sequencing of replayable transactions.
                uint256 public constant REPLAYABLE_NONCE_KEY = 8453;
                /// @notice Thrown when `initialize` is called but the account already has had at least one owner.
                error Initialized();
                /// @notice Thrown when a call is passed to `executeWithoutChainIdValidation` that is not allowed by
                ///         `canSkipChainIdValidation`
                ///
                /// @param selector The selector of the call.
                error SelectorNotAllowed(bytes4 selector);
                /// @notice Thrown in validateUserOp if the key of `UserOperation.nonce` does not match the calldata.
                ///
                /// @dev Calls to `this.executeWithoutChainIdValidation` MUST use `REPLAYABLE_NONCE_KEY` and
                ///      calls NOT to `this.executeWithoutChainIdValidation` MUST NOT use `REPLAYABLE_NONCE_KEY`.
                ///
                /// @param key The invalid `UserOperation.nonce` key.
                error InvalidNonceKey(uint256 key);
                /// @notice Reverts if the caller is not the EntryPoint.
                modifier onlyEntryPoint() virtual {
                    if (msg.sender != entryPoint()) {
                        revert Unauthorized();
                    }
                    _;
                }
                /// @notice Reverts if the caller is neither the EntryPoint, the owner, nor the account itself.
                modifier onlyEntryPointOrOwner() virtual {
                    if (msg.sender != entryPoint()) {
                        _checkOwner();
                    }
                    _;
                }
                /// @notice Sends to the EntryPoint (i.e. `msg.sender`) the missing funds for this transaction.
                ///
                /// @dev Subclass MAY override this modifier for better funds management (e.g. send to the
                ///      EntryPoint more than the minimum required, so that in future transactions it will not
                ///      be required to send again).
                ///
                /// @param missingAccountFunds The minimum value this modifier should send the EntryPoint which
                ///                            MAY be zero, in case there is enough deposit, or the userOp has a
                ///                            paymaster.
                modifier payPrefund(uint256 missingAccountFunds) virtual {
                    _;
                    assembly ("memory-safe") {
                        if missingAccountFunds {
                            // Ignore failure (it's EntryPoint's job to verify, not the account's).
                            pop(call(gas(), caller(), missingAccountFunds, codesize(), 0x00, codesize(), 0x00))
                        }
                    }
                }
                constructor() {
                    // Implementation should not be initializable (does not affect proxies which use their own storage).
                    bytes[] memory owners = new bytes[](1);
                    owners[0] = abi.encode(address(0));
                    _initializeOwners(owners);
                }
                /// @notice Initializes the account with the `owners`.
                ///
                /// @dev Reverts if the account has had at least one owner, i.e. has been initialized.
                ///
                /// @param owners Array of initial owners for this account. Each item should be
                ///               an ABI encoded Ethereum address, i.e. 32 bytes with 12 leading 0 bytes,
                ///               or a 64 byte public key.
                function initialize(bytes[] calldata owners) external payable virtual {
                    if (nextOwnerIndex() != 0) {
                        revert Initialized();
                    }
                    _initializeOwners(owners);
                }
                /// @inheritdoc IAccount
                ///
                /// @notice ERC-4337 `validateUserOp` method. The EntryPoint will
                ///         call `UserOperation.sender.call(UserOperation.callData)` only if this validation call returns
                ///         successfully.
                ///
                /// @dev Signature failure should be reported by returning 1 (see: `this._isValidSignature`). This
                ///      allows making a "simulation call" without a valid signature. Other failures (e.g. invalid signature format)
                ///      should still revert to signal failure.
                /// @dev Reverts if the `UserOperation.nonce` key is invalid for `UserOperation.calldata`.
                /// @dev Reverts if the signature format is incorrect or invalid for owner type.
                ///
                /// @param userOp              The `UserOperation` to validate.
                /// @param userOpHash          The `UserOperation` hash, as computed by `EntryPoint.getUserOpHash(UserOperation)`.
                /// @param missingAccountFunds The missing account funds that must be deposited on the Entrypoint.
                ///
                /// @return validationData The encoded `ValidationData` structure:
                ///                        `(uint256(validAfter) << (160 + 48)) | (uint256(validUntil) << 160) | (success ? 0 : 1)`
                ///                        where `validUntil` is 0 (indefinite) and `validAfter` is 0.
                function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds)
                    external
                    virtual
                    onlyEntryPoint
                    payPrefund(missingAccountFunds)
                    returns (uint256 validationData)
                {
                    uint256 key = userOp.nonce >> 64;
                    if (bytes4(userOp.callData) == this.executeWithoutChainIdValidation.selector) {
                        userOpHash = getUserOpHashWithoutChainId(userOp);
                        if (key != REPLAYABLE_NONCE_KEY) {
                            revert InvalidNonceKey(key);
                        }
                    } else {
                        if (key == REPLAYABLE_NONCE_KEY) {
                            revert InvalidNonceKey(key);
                        }
                    }
                    // Return 0 if the recovered address matches the owner.
                    if (_isValidSignature(userOpHash, userOp.signature)) {
                        return 0;
                    }
                    // Else return 1
                    return 1;
                }
                /// @notice Executes `calls` on this account (i.e. self call).
                ///
                /// @dev Can only be called by the Entrypoint.
                /// @dev Reverts if the given call is not authorized to skip the chain ID validtion.
                /// @dev `validateUserOp()` will recompute the `userOpHash` without the chain ID before validating
                ///      it if the `UserOperation.calldata` is calling this function. This allows certain UserOperations
                ///      to be replayed for all accounts sharing the same address across chains. E.g. This may be
                ///      useful for syncing owner changes.
                ///
                /// @param calls An array of calldata to use for separate self calls.
                function executeWithoutChainIdValidation(bytes[] calldata calls) external payable virtual onlyEntryPoint {
                    for (uint256 i; i < calls.length; i++) {
                        bytes calldata call = calls[i];
                        bytes4 selector = bytes4(call);
                        if (!canSkipChainIdValidation(selector)) {
                            revert SelectorNotAllowed(selector);
                        }
                        _call(address(this), 0, call);
                    }
                }
                /// @notice Executes the given call from this account.
                ///
                /// @dev Can only be called by the Entrypoint or an owner of this account (including itself).
                ///
                /// @param target The address to call.
                /// @param value  The value to send with the call.
                /// @param data   The data of the call.
                function execute(address target, uint256 value, bytes calldata data)
                    external
                    payable
                    virtual
                    onlyEntryPointOrOwner
                {
                    _call(target, value, data);
                }
                /// @notice Executes batch of `Call`s.
                ///
                /// @dev Can only be called by the Entrypoint or an owner of this account (including itself).
                ///
                /// @param calls The list of `Call`s to execute.
                function executeBatch(Call[] calldata calls) external payable virtual onlyEntryPointOrOwner {
                    for (uint256 i; i < calls.length; i++) {
                        _call(calls[i].target, calls[i].value, calls[i].data);
                    }
                }
                /// @notice Returns the address of the EntryPoint v0.6.
                ///
                /// @return The address of the EntryPoint v0.6
                function entryPoint() public view virtual returns (address) {
                    return 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789;
                }
                /// @notice Computes the hash of the `UserOperation` in the same way as EntryPoint v0.6, but
                ///         leaves out the chain ID.
                ///
                /// @dev This allows accounts to sign a hash that can be used on many chains.
                ///
                /// @param userOp The `UserOperation` to compute the hash for.
                ///
                /// @return The `UserOperation` hash, which does not depend on chain ID.
                function getUserOpHashWithoutChainId(UserOperation calldata userOp) public view virtual returns (bytes32) {
                    return keccak256(abi.encode(UserOperationLib.hash(userOp), entryPoint()));
                }
                /// @notice Returns the implementation of the ERC1967 proxy.
                ///
                /// @return $ The address of implementation contract.
                function implementation() public view returns (address $) {
                    assembly {
                        $ := sload(_ERC1967_IMPLEMENTATION_SLOT)
                    }
                }
                /// @notice Returns whether `functionSelector` can be called in `executeWithoutChainIdValidation`.
                ///
                /// @param functionSelector The function selector to check.
                ////
                /// @return `true` is the function selector is allowed to skip the chain ID validation, else `false`.
                function canSkipChainIdValidation(bytes4 functionSelector) public pure returns (bool) {
                    if (
                        functionSelector == MultiOwnable.addOwnerPublicKey.selector
                            || functionSelector == MultiOwnable.addOwnerAddress.selector
                            || functionSelector == MultiOwnable.removeOwnerAtIndex.selector
                            || functionSelector == MultiOwnable.removeLastOwner.selector
                            || functionSelector == UUPSUpgradeable.upgradeToAndCall.selector
                    ) {
                        return true;
                    }
                    return false;
                }
                /// @notice Executes the given call from this account.
                ///
                /// @dev Reverts if the call reverted.
                /// @dev Implementation taken from
                /// https://github.com/alchemyplatform/light-account/blob/43f625afdda544d5e5af9c370c9f4be0943e4e90/src/common/BaseLightAccount.sol#L125
                ///
                /// @param target The target call address.
                /// @param value  The call value to user.
                /// @param data   The raw call data.
                function _call(address target, uint256 value, bytes memory data) internal {
                    (bool success, bytes memory result) = target.call{value: value}(data);
                    if (!success) {
                        assembly ("memory-safe") {
                            revert(add(result, 32), mload(result))
                        }
                    }
                }
                /// @inheritdoc ERC1271
                ///
                /// @dev Used by both `ERC1271.isValidSignature` AND `IAccount.validateUserOp` signature validation.
                /// @dev Reverts if owner at `ownerIndex` is not compatible with `signature` format.
                ///
                /// @param signature ABI encoded `SignatureWrapper`.
                function _isValidSignature(bytes32 hash, bytes calldata signature) internal view virtual override returns (bool) {
                    SignatureWrapper memory sigWrapper = abi.decode(signature, (SignatureWrapper));
                    bytes memory ownerBytes = ownerAtIndex(sigWrapper.ownerIndex);
                    if (ownerBytes.length == 32) {
                        if (uint256(bytes32(ownerBytes)) > type(uint160).max) {
                            // technically should be impossible given owners can only be added with
                            // addOwnerAddress and addOwnerPublicKey, but we leave incase of future changes.
                            revert InvalidEthereumAddressOwner(ownerBytes);
                        }
                        address owner;
                        assembly ("memory-safe") {
                            owner := mload(add(ownerBytes, 32))
                        }
                        return SignatureCheckerLib.isValidSignatureNow(owner, hash, sigWrapper.signatureData);
                    }
                    if (ownerBytes.length == 64) {
                        (uint256 x, uint256 y) = abi.decode(ownerBytes, (uint256, uint256));
                        WebAuthn.WebAuthnAuth memory auth = abi.decode(sigWrapper.signatureData, (WebAuthn.WebAuthnAuth));
                        return WebAuthn.verify({challenge: abi.encode(hash), requireUV: false, webAuthnAuth: auth, x: x, y: y});
                    }
                    revert InvalidOwnerBytesLength(ownerBytes);
                }
                /// @inheritdoc UUPSUpgradeable
                ///
                /// @dev Authorization logic is only based on the `msg.sender` being an owner of this account,
                ///      or `address(this)`.
                function _authorizeUpgrade(address) internal view virtual override(UUPSUpgradeable) onlyOwner {}
                /// @inheritdoc ERC1271
                function _domainNameAndVersion() internal pure override(ERC1271) returns (string memory, string memory) {
                    return ("Coinbase Smart Wallet", "1");
                }
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.8.12;
            import "./UserOperation.sol";
            interface IAccount {
                /**
                 * Validate user's signature and nonce
                 * the entryPoint will make the call to the recipient only if this validation call returns successfully.
                 * signature failure should be reported by returning SIG_VALIDATION_FAILED (1).
                 * This allows making a "simulation call" without a valid signature
                 * Other failures (e.g. nonce mismatch, or invalid signature format) should still revert to signal failure.
                 *
                 * @dev Must validate caller is the entryPoint.
                 *      Must validate the signature and nonce
                 * @param userOp the operation that is about to be executed.
                 * @param userOpHash hash of the user's request data. can be used as the basis for signature.
                 * @param missingAccountFunds missing funds on the account's deposit in the entrypoint.
                 *      This is the minimum amount to transfer to the sender(entryPoint) to be able to make the call.
                 *      The excess is left as a deposit in the entrypoint, for future calls.
                 *      can be withdrawn anytime using "entryPoint.withdrawTo()"
                 *      In case there is a paymaster in the request (or the current deposit is high enough), this value will be zero.
                 * @return validationData packaged ValidationData structure. use `_packValidationData` and `_unpackValidationData` to encode and decode
                 *      <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure,
                 *         otherwise, an address of an "authorizer" contract.
                 *      <6-byte> validUntil - last timestamp this operation is valid. 0 for "indefinite"
                 *      <6-byte> validAfter - first timestamp this operation is valid
                 *      If an account doesn't use time-range, it is enough to return SIG_VALIDATION_FAILED value (1) for signature failure.
                 *      Note that the validation code cannot use block.timestamp (or block.number) directly.
                 */
                function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds)
                external returns (uint256 validationData);
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.8.12;
            /* solhint-disable no-inline-assembly */
            import {calldataKeccak} from "../core/Helpers.sol";
            /**
             * User Operation struct
             * @param sender the sender account of this request.
                 * @param nonce unique value the sender uses to verify it is not a replay.
                 * @param initCode if set, the account contract will be created by this constructor/
                 * @param callData the method call to execute on this account.
                 * @param callGasLimit the gas limit passed to the callData method call.
                 * @param verificationGasLimit gas used for validateUserOp and validatePaymasterUserOp.
                 * @param preVerificationGas gas not calculated by the handleOps method, but added to the gas paid. Covers batch overhead.
                 * @param maxFeePerGas same as EIP-1559 gas parameter.
                 * @param maxPriorityFeePerGas same as EIP-1559 gas parameter.
                 * @param paymasterAndData if set, this field holds the paymaster address and paymaster-specific data. the paymaster will pay for the transaction instead of the sender.
                 * @param signature sender-verified signature over the entire request, the EntryPoint address and the chain ID.
                 */
                struct UserOperation {
                    address sender;
                    uint256 nonce;
                    bytes initCode;
                    bytes callData;
                    uint256 callGasLimit;
                    uint256 verificationGasLimit;
                    uint256 preVerificationGas;
                    uint256 maxFeePerGas;
                    uint256 maxPriorityFeePerGas;
                    bytes paymasterAndData;
                    bytes signature;
                }
            /**
             * Utility functions helpful when working with UserOperation structs.
             */
            library UserOperationLib {
                function getSender(UserOperation calldata userOp) internal pure returns (address) {
                    address data;
                    //read sender from userOp, which is first userOp member (saves 800 gas...)
                    assembly {data := calldataload(userOp)}
                    return address(uint160(data));
                }
                //relayer/block builder might submit the TX with higher priorityFee, but the user should not
                // pay above what he signed for.
                function gasPrice(UserOperation calldata userOp) internal view returns (uint256) {
                unchecked {
                    uint256 maxFeePerGas = userOp.maxFeePerGas;
                    uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas;
                    if (maxFeePerGas == maxPriorityFeePerGas) {
                        //legacy mode (for networks that don't support basefee opcode)
                        return maxFeePerGas;
                    }
                    return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee);
                }
                }
                function pack(UserOperation calldata userOp) internal pure returns (bytes memory ret) {
                    address sender = getSender(userOp);
                    uint256 nonce = userOp.nonce;
                    bytes32 hashInitCode = calldataKeccak(userOp.initCode);
                    bytes32 hashCallData = calldataKeccak(userOp.callData);
                    uint256 callGasLimit = userOp.callGasLimit;
                    uint256 verificationGasLimit = userOp.verificationGasLimit;
                    uint256 preVerificationGas = userOp.preVerificationGas;
                    uint256 maxFeePerGas = userOp.maxFeePerGas;
                    uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas;
                    bytes32 hashPaymasterAndData = calldataKeccak(userOp.paymasterAndData);
                    return abi.encode(
                        sender, nonce,
                        hashInitCode, hashCallData,
                        callGasLimit, verificationGasLimit, preVerificationGas,
                        maxFeePerGas, maxPriorityFeePerGas,
                        hashPaymasterAndData
                    );
                }
                function hash(UserOperation calldata userOp) internal pure returns (bytes32) {
                    return keccak256(pack(userOp));
                }
                function min(uint256 a, uint256 b) internal pure returns (uint256) {
                    return a < b ? a : b;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.4;
            /// @notice Receiver mixin for ETH and safe-transferred ERC721 and ERC1155 tokens.
            /// @author Solady (https://github.com/Vectorized/solady/blob/main/src/accounts/Receiver.sol)
            ///
            /// @dev Note:
            /// - Handles all ERC721 and ERC1155 token safety callbacks.
            /// - Collapses function table gas overhead and code size.
            /// - Utilizes fallback so unknown calldata will pass on.
            abstract contract Receiver {
                /// @dev For receiving ETH.
                receive() external payable virtual {}
                /// @dev Fallback function with the `receiverFallback` modifier.
                fallback() external payable virtual receiverFallback {}
                /// @dev Modifier for the fallback function to handle token callbacks.
                modifier receiverFallback() virtual {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let s := shr(224, calldataload(0))
                        // 0x150b7a02: `onERC721Received(address,address,uint256,bytes)`.
                        // 0xf23a6e61: `onERC1155Received(address,address,uint256,uint256,bytes)`.
                        // 0xbc197c81: `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`.
                        if or(eq(s, 0x150b7a02), or(eq(s, 0xf23a6e61), eq(s, 0xbc197c81))) {
                            mstore(0x20, s) // Store `msg.sig`.
                            return(0x3c, 0x20) // Return `msg.sig`.
                        }
                    }
                    _;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.4;
            /// @notice Signature verification helper that supports both ECDSA signatures from EOAs
            /// and ERC1271 signatures from smart contract wallets like Argent and Gnosis safe.
            /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SignatureCheckerLib.sol)
            /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/SignatureChecker.sol)
            ///
            /// @dev Note:
            /// - The signature checking functions use the ecrecover precompile (0x1).
            /// - The `bytes memory signature` variants use the identity precompile (0x4)
            ///   to copy memory internally.
            /// - Unlike ECDSA signatures, contract signatures are revocable.
            /// - 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 SignatureCheckerLib {
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*               SIGNATURE CHECKING OPERATIONS                */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Returns whether `signature` is valid for `signer` and `hash`.
                /// If `signer` is a smart contract, the signature is validated with ERC1271.
                /// Otherwise, the signature is validated with `ECDSA.recover`.
                function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature)
                    internal
                    view
                    returns (bool isValid)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Clean the upper 96 bits of `signer` in case they are dirty.
                        for { signer := shr(96, shl(96, signer)) } signer {} {
                            let m := mload(0x40)
                            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`.
                                let t :=
                                    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(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                                    isValid := 1
                                    mstore(0x60, 0) // Restore the zero slot.
                                    mstore(0x40, m) // Restore the free memory pointer.
                                    break
                                }
                            }
                            if eq(mload(signature), 65) {
                                mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                                mstore(0x60, mload(add(signature, 0x40))) // `s`.
                                let t :=
                                    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(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                                    isValid := 1
                                    mstore(0x60, 0) // Restore the zero slot.
                                    mstore(0x40, m) // Restore the free memory pointer.
                                    break
                                }
                            }
                            mstore(0x60, 0) // Restore the zero slot.
                            mstore(0x40, m) // Restore the free memory pointer.
                            let f := shl(224, 0x1626ba7e)
                            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                            mstore(add(m, 0x04), hash)
                            let d := add(m, 0x24)
                            mstore(d, 0x40) // The offset of the `signature` in the calldata.
                            // Copy the `signature` over.
                            let n := add(0x20, mload(signature))
                            pop(staticcall(gas(), 4, signature, n, add(m, 0x44), n))
                            // forgefmt: disable-next-item
                            isValid := and(
                                // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                                eq(mload(d), f),
                                // Whether the staticcall does not revert.
                                // This must be placed at the end of the `and` clause,
                                // as the arguments are evaluated from right to left.
                                staticcall(
                                    gas(), // Remaining gas.
                                    signer, // The `signer` address.
                                    m, // Offset of calldata in memory.
                                    add(returndatasize(), 0x44), // Length of calldata in memory.
                                    d, // Offset of returndata.
                                    0x20 // Length of returndata to write.
                                )
                            )
                            break
                        }
                    }
                }
                /// @dev Returns whether `signature` is valid for `signer` and `hash`.
                /// If `signer` is a smart contract, the signature is validated with ERC1271.
                /// Otherwise, the signature is validated with `ECDSA.recover`.
                function isValidSignatureNowCalldata(address signer, bytes32 hash, bytes calldata signature)
                    internal
                    view
                    returns (bool isValid)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Clean the upper 96 bits of `signer` in case they are dirty.
                        for { signer := shr(96, shl(96, signer)) } signer {} {
                            let m := mload(0x40)
                            mstore(0x00, hash)
                            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`.
                                let t :=
                                    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(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                                    isValid := 1
                                    mstore(0x60, 0) // Restore the zero slot.
                                    mstore(0x40, m) // Restore the free memory pointer.
                                    break
                                }
                            }
                            if eq(signature.length, 65) {
                                mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                                calldatacopy(0x40, signature.offset, 0x40) // `r`, `s`.
                                let t :=
                                    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(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                                    isValid := 1
                                    mstore(0x60, 0) // Restore the zero slot.
                                    mstore(0x40, m) // Restore the free memory pointer.
                                    break
                                }
                            }
                            mstore(0x60, 0) // Restore the zero slot.
                            mstore(0x40, m) // Restore the free memory pointer.
                            let f := shl(224, 0x1626ba7e)
                            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                            mstore(add(m, 0x04), hash)
                            let d := add(m, 0x24)
                            mstore(d, 0x40) // The offset of the `signature` in the calldata.
                            mstore(add(m, 0x44), signature.length)
                            // Copy the `signature` over.
                            calldatacopy(add(m, 0x64), signature.offset, signature.length)
                            // forgefmt: disable-next-item
                            isValid := and(
                                // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                                eq(mload(d), f),
                                // Whether the staticcall does not revert.
                                // This must be placed at the end of the `and` clause,
                                // as the arguments are evaluated from right to left.
                                staticcall(
                                    gas(), // Remaining gas.
                                    signer, // The `signer` address.
                                    m, // Offset of calldata in memory.
                                    add(signature.length, 0x64), // Length of calldata in memory.
                                    d, // Offset of returndata.
                                    0x20 // Length of returndata to write.
                                )
                            )
                            break
                        }
                    }
                }
                /// @dev Returns whether the signature (`r`, `vs`) is valid for `signer` and `hash`.
                /// If `signer` is a smart contract, the signature is validated with ERC1271.
                /// Otherwise, the signature is validated with `ECDSA.recover`.
                function isValidSignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs)
                    internal
                    view
                    returns (bool isValid)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Clean the upper 96 bits of `signer` in case they are dirty.
                        for { signer := shr(96, shl(96, signer)) } signer {} {
                            let m := mload(0x40)
                            mstore(0x00, hash)
                            mstore(0x20, add(shr(255, vs), 27)) // `v`.
                            mstore(0x40, r) // `r`.
                            mstore(0x60, shr(1, shl(1, vs))) // `s`.
                            let t :=
                                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(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                                isValid := 1
                                mstore(0x60, 0) // Restore the zero slot.
                                mstore(0x40, m) // Restore the free memory pointer.
                                break
                            }
                            let f := shl(224, 0x1626ba7e)
                            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                            mstore(add(m, 0x04), hash)
                            let d := add(m, 0x24)
                            mstore(d, 0x40) // The offset of the `signature` in the calldata.
                            mstore(add(m, 0x44), 65) // Length of the signature.
                            mstore(add(m, 0x64), r) // `r`.
                            mstore(add(m, 0x84), mload(0x60)) // `s`.
                            mstore8(add(m, 0xa4), mload(0x20)) // `v`.
                            // forgefmt: disable-next-item
                            isValid := and(
                                // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                                eq(mload(d), f),
                                // Whether the staticcall does not revert.
                                // This must be placed at the end of the `and` clause,
                                // as the arguments are evaluated from right to left.
                                staticcall(
                                    gas(), // Remaining gas.
                                    signer, // The `signer` address.
                                    m, // Offset of calldata in memory.
                                    0xa5, // Length of calldata in memory.
                                    d, // Offset of returndata.
                                    0x20 // Length of returndata to write.
                                )
                            )
                            mstore(0x60, 0) // Restore the zero slot.
                            mstore(0x40, m) // Restore the free memory pointer.
                            break
                        }
                    }
                }
                /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `signer` and `hash`.
                /// If `signer` is a smart contract, the signature is validated with ERC1271.
                /// Otherwise, the signature is validated with `ECDSA.recover`.
                function isValidSignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s)
                    internal
                    view
                    returns (bool isValid)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Clean the upper 96 bits of `signer` in case they are dirty.
                        for { signer := shr(96, shl(96, signer)) } signer {} {
                            let m := mload(0x40)
                            mstore(0x00, hash)
                            mstore(0x20, and(v, 0xff)) // `v`.
                            mstore(0x40, r) // `r`.
                            mstore(0x60, s) // `s`.
                            let t :=
                                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(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                                isValid := 1
                                mstore(0x60, 0) // Restore the zero slot.
                                mstore(0x40, m) // Restore the free memory pointer.
                                break
                            }
                            let f := shl(224, 0x1626ba7e)
                            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                            mstore(add(m, 0x04), hash)
                            let d := add(m, 0x24)
                            mstore(d, 0x40) // The offset of the `signature` in the calldata.
                            mstore(add(m, 0x44), 65) // Length of the signature.
                            mstore(add(m, 0x64), r) // `r`.
                            mstore(add(m, 0x84), s) // `s`.
                            mstore8(add(m, 0xa4), v) // `v`.
                            // forgefmt: disable-next-item
                            isValid := and(
                                // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                                eq(mload(d), f),
                                // Whether the staticcall does not revert.
                                // This must be placed at the end of the `and` clause,
                                // as the arguments are evaluated from right to left.
                                staticcall(
                                    gas(), // Remaining gas.
                                    signer, // The `signer` address.
                                    m, // Offset of calldata in memory.
                                    0xa5, // Length of calldata in memory.
                                    d, // Offset of returndata.
                                    0x20 // Length of returndata to write.
                                )
                            )
                            mstore(0x60, 0) // Restore the zero slot.
                            mstore(0x40, m) // Restore the free memory pointer.
                            break
                        }
                    }
                }
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                     ERC1271 OPERATIONS                     */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract.
                function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes memory signature)
                    internal
                    view
                    returns (bool isValid)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let m := mload(0x40)
                        let f := shl(224, 0x1626ba7e)
                        mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                        mstore(add(m, 0x04), hash)
                        let d := add(m, 0x24)
                        mstore(d, 0x40) // The offset of the `signature` in the calldata.
                        // Copy the `signature` over.
                        let n := add(0x20, mload(signature))
                        pop(staticcall(gas(), 4, signature, n, add(m, 0x44), n))
                        // forgefmt: disable-next-item
                        isValid := and(
                            // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                            eq(mload(d), f),
                            // Whether the staticcall does not revert.
                            // This must be placed at the end of the `and` clause,
                            // as the arguments are evaluated from right to left.
                            staticcall(
                                gas(), // Remaining gas.
                                signer, // The `signer` address.
                                m, // Offset of calldata in memory.
                                add(returndatasize(), 0x44), // Length of calldata in memory.
                                d, // Offset of returndata.
                                0x20 // Length of returndata to write.
                            )
                        )
                    }
                }
                /// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract.
                function isValidERC1271SignatureNowCalldata(
                    address signer,
                    bytes32 hash,
                    bytes calldata signature
                ) internal view returns (bool isValid) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let m := mload(0x40)
                        let f := shl(224, 0x1626ba7e)
                        mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                        mstore(add(m, 0x04), hash)
                        let d := add(m, 0x24)
                        mstore(d, 0x40) // The offset of the `signature` in the calldata.
                        mstore(add(m, 0x44), signature.length)
                        // Copy the `signature` over.
                        calldatacopy(add(m, 0x64), signature.offset, signature.length)
                        // forgefmt: disable-next-item
                        isValid := and(
                            // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                            eq(mload(d), f),
                            // Whether the staticcall does not revert.
                            // This must be placed at the end of the `and` clause,
                            // as the arguments are evaluated from right to left.
                            staticcall(
                                gas(), // Remaining gas.
                                signer, // The `signer` address.
                                m, // Offset of calldata in memory.
                                add(signature.length, 0x64), // Length of calldata in memory.
                                d, // Offset of returndata.
                                0x20 // Length of returndata to write.
                            )
                        )
                    }
                }
                /// @dev Returns whether the signature (`r`, `vs`) is valid for `hash`
                /// for an ERC1271 `signer` contract.
                function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs)
                    internal
                    view
                    returns (bool isValid)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let m := mload(0x40)
                        let f := shl(224, 0x1626ba7e)
                        mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                        mstore(add(m, 0x04), hash)
                        let d := add(m, 0x24)
                        mstore(d, 0x40) // The offset of the `signature` in the calldata.
                        mstore(add(m, 0x44), 65) // Length of the signature.
                        mstore(add(m, 0x64), r) // `r`.
                        mstore(add(m, 0x84), shr(1, shl(1, vs))) // `s`.
                        mstore8(add(m, 0xa4), add(shr(255, vs), 27)) // `v`.
                        // forgefmt: disable-next-item
                        isValid := and(
                            // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                            eq(mload(d), f),
                            // Whether the staticcall does not revert.
                            // This must be placed at the end of the `and` clause,
                            // as the arguments are evaluated from right to left.
                            staticcall(
                                gas(), // Remaining gas.
                                signer, // The `signer` address.
                                m, // Offset of calldata in memory.
                                0xa5, // Length of calldata in memory.
                                d, // Offset of returndata.
                                0x20 // Length of returndata to write.
                            )
                        )
                    }
                }
                /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `hash`
                /// for an ERC1271 `signer` contract.
                function isValidERC1271SignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s)
                    internal
                    view
                    returns (bool isValid)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let m := mload(0x40)
                        let f := shl(224, 0x1626ba7e)
                        mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                        mstore(add(m, 0x04), hash)
                        let d := add(m, 0x24)
                        mstore(d, 0x40) // The offset of the `signature` in the calldata.
                        mstore(add(m, 0x44), 65) // Length of the signature.
                        mstore(add(m, 0x64), r) // `r`.
                        mstore(add(m, 0x84), s) // `s`.
                        mstore8(add(m, 0xa4), v) // `v`.
                        // forgefmt: disable-next-item
                        isValid := and(
                            // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                            eq(mload(d), f),
                            // Whether the staticcall does not revert.
                            // This must be placed at the end of the `and` clause,
                            // as the arguments are evaluated from right to left.
                            staticcall(
                                gas(), // Remaining gas.
                                signer, // The `signer` address.
                                m, // Offset of calldata in memory.
                                0xa5, // Length of calldata in memory.
                                d, // Offset of returndata.
                                0x20 // Length of returndata to write.
                            )
                        )
                    }
                }
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                     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 UUPS proxy mixin.
            /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/UUPSUpgradeable.sol)
            /// @author Modified from OpenZeppelin
            /// (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/utils/UUPSUpgradeable.sol)
            ///
            /// Note:
            /// - This implementation is intended to be used with ERC1967 proxies.
            /// See: `LibClone.deployERC1967` and related functions.
            /// - This implementation is NOT compatible with legacy OpenZeppelin proxies
            /// which do not store the implementation at `_ERC1967_IMPLEMENTATION_SLOT`.
            abstract contract UUPSUpgradeable {
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                       CUSTOM ERRORS                        */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev The upgrade failed.
                error UpgradeFailed();
                /// @dev The call is from an unauthorized call context.
                error UnauthorizedCallContext();
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                         IMMUTABLES                         */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev For checking if the context is a delegate call.
                uint256 private immutable __self = uint256(uint160(address(this)));
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                           EVENTS                           */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Emitted when the proxy's implementation is upgraded.
                event Upgraded(address indexed implementation);
                /// @dev `keccak256(bytes("Upgraded(address)"))`.
                uint256 private constant _UPGRADED_EVENT_SIGNATURE =
                    0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b;
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                          STORAGE                           */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev The ERC-1967 storage slot for the implementation in the proxy.
                /// `uint256(keccak256("eip1967.proxy.implementation")) - 1`.
                bytes32 internal constant _ERC1967_IMPLEMENTATION_SLOT =
                    0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                      UUPS OPERATIONS                       */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Please override this function to check if `msg.sender` is authorized
                /// to upgrade the proxy to `newImplementation`, reverting if not.
                /// ```
                ///     function _authorizeUpgrade(address) internal override onlyOwner {}
                /// ```
                function _authorizeUpgrade(address newImplementation) internal virtual;
                /// @dev Returns the storage slot used by the implementation,
                /// as specified in [ERC1822](https://eips.ethereum.org/EIPS/eip-1822).
                ///
                /// Note: The `notDelegated` modifier prevents accidental upgrades to
                /// an implementation that is a proxy contract.
                function proxiableUUID() public view virtual notDelegated returns (bytes32) {
                    // This function must always return `_ERC1967_IMPLEMENTATION_SLOT` to comply with ERC1967.
                    return _ERC1967_IMPLEMENTATION_SLOT;
                }
                /// @dev Upgrades the proxy's implementation to `newImplementation`.
                /// Emits a {Upgraded} event.
                ///
                /// Note: Passing in empty `data` skips the delegatecall to `newImplementation`.
                function upgradeToAndCall(address newImplementation, bytes calldata data)
                    public
                    payable
                    virtual
                    onlyProxy
                {
                    _authorizeUpgrade(newImplementation);
                    /// @solidity memory-safe-assembly
                    assembly {
                        newImplementation := shr(96, shl(96, newImplementation)) // Clears upper 96 bits.
                        mstore(0x01, 0x52d1902d) // `proxiableUUID()`.
                        let s := _ERC1967_IMPLEMENTATION_SLOT
                        // Check if `newImplementation` implements `proxiableUUID` correctly.
                        if iszero(eq(mload(staticcall(gas(), newImplementation, 0x1d, 0x04, 0x01, 0x20)), s)) {
                            mstore(0x01, 0x55299b49) // `UpgradeFailed()`.
                            revert(0x1d, 0x04)
                        }
                        // Emit the {Upgraded} event.
                        log2(codesize(), 0x00, _UPGRADED_EVENT_SIGNATURE, newImplementation)
                        sstore(s, newImplementation) // Updates the implementation.
                        // Perform a delegatecall to `newImplementation` if `data` is non-empty.
                        if data.length {
                            // Forwards the `data` to `newImplementation` via delegatecall.
                            let m := mload(0x40)
                            calldatacopy(m, data.offset, data.length)
                            if iszero(delegatecall(gas(), newImplementation, m, data.length, codesize(), 0x00))
                            {
                                // Bubble up the revert if the call reverts.
                                returndatacopy(m, 0x00, returndatasize())
                                revert(m, returndatasize())
                            }
                        }
                    }
                }
                /// @dev Requires that the execution is performed through a proxy.
                modifier onlyProxy() {
                    uint256 s = __self;
                    /// @solidity memory-safe-assembly
                    assembly {
                        // To enable use cases with an immutable default implementation in the bytecode,
                        // (see: ERC6551Proxy), we don't require that the proxy address must match the
                        // value stored in the implementation slot, which may not be initialized.
                        if eq(s, address()) {
                            mstore(0x00, 0x9f03a026) // `UnauthorizedCallContext()`.
                            revert(0x1c, 0x04)
                        }
                    }
                    _;
                }
                /// @dev Requires that the execution is NOT performed via delegatecall.
                /// This is the opposite of `onlyProxy`.
                modifier notDelegated() {
                    uint256 s = __self;
                    /// @solidity memory-safe-assembly
                    assembly {
                        if iszero(eq(s, address())) {
                            mstore(0x00, 0x9f03a026) // `UnauthorizedCallContext()`.
                            revert(0x1c, 0x04)
                        }
                    }
                    _;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.0;
            import {FCL_ecdsa} from "FreshCryptoLib/FCL_ecdsa.sol";
            import {FCL_Elliptic_ZZ} from "FreshCryptoLib/FCL_elliptic.sol";
            import {Base64} from "openzeppelin-contracts/contracts/utils/Base64.sol";
            import {LibString} from "solady/utils/LibString.sol";
            /// @title WebAuthn
            ///
            /// @notice A library for verifying WebAuthn Authentication Assertions, built off the work
            ///         of Daimo.
            ///
            /// @dev Attempts to use the RIP-7212 precompile for signature verification.
            ///      If precompile verification fails, it falls back to FreshCryptoLib.
            ///
            /// @author Coinbase (https://github.com/base-org/webauthn-sol)
            /// @author Daimo (https://github.com/daimo-eth/p256-verifier/blob/master/src/WebAuthn.sol)
            library WebAuthn {
                using LibString for string;
                struct WebAuthnAuth {
                    /// @dev The WebAuthn authenticator data.
                    ///      See https://www.w3.org/TR/webauthn-2/#dom-authenticatorassertionresponse-authenticatordata.
                    bytes authenticatorData;
                    /// @dev The WebAuthn client data JSON.
                    ///      See https://www.w3.org/TR/webauthn-2/#dom-authenticatorresponse-clientdatajson.
                    string clientDataJSON;
                    /// @dev The index at which "challenge":"..." occurs in `clientDataJSON`.
                    uint256 challengeIndex;
                    /// @dev The index at which "type":"..." occurs in `clientDataJSON`.
                    uint256 typeIndex;
                    /// @dev The r value of secp256r1 signature
                    uint256 r;
                    /// @dev The s value of secp256r1 signature
                    uint256 s;
                }
                /// @dev Bit 0 of the authenticator data struct, corresponding to the "User Present" bit.
                ///      See https://www.w3.org/TR/webauthn-2/#flags.
                bytes1 private constant _AUTH_DATA_FLAGS_UP = 0x01;
                /// @dev Bit 2 of the authenticator data struct, corresponding to the "User Verified" bit.
                ///      See https://www.w3.org/TR/webauthn-2/#flags.
                bytes1 private constant _AUTH_DATA_FLAGS_UV = 0x04;
                /// @dev Secp256r1 curve order / 2 used as guard to prevent signature malleability issue.
                uint256 private constant _P256_N_DIV_2 = FCL_Elliptic_ZZ.n / 2;
                /// @dev The precompiled contract address to use for signature verification in the “secp256r1” elliptic curve.
                ///      See https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md.
                address private constant _VERIFIER = address(0x100);
                /// @dev The expected type (hash) in the client data JSON when verifying assertion signatures.
                ///      See https://www.w3.org/TR/webauthn-2/#dom-collectedclientdata-type
                bytes32 private constant _EXPECTED_TYPE_HASH = keccak256('"type":"webauthn.get"');
                ///
                /// @notice Verifies a Webauthn Authentication Assertion as described
                /// in https://www.w3.org/TR/webauthn-2/#sctn-verifying-assertion.
                ///
                /// @dev We do not verify all the steps as described in the specification, only ones relevant to our context.
                ///      Please carefully read through this list before usage.
                ///
                ///      Specifically, we do verify the following:
                ///         - Verify that authenticatorData (which comes from the authenticator, such as iCloud Keychain) indicates
                ///           a well-formed assertion with the user present bit set. If `requireUV` is set, checks that the authenticator
                ///           enforced user verification. User verification should be required if, and only if, options.userVerification
                ///           is set to required in the request.
                ///         - Verifies that the client JSON is of type "webauthn.get", i.e. the client was responding to a request to
                ///           assert authentication.
                ///         - Verifies that the client JSON contains the requested challenge.
                ///         - Verifies that (r, s) constitute a valid signature over both the authenicatorData and client JSON, for public
                ///            key (x, y).
                ///
                ///      We make some assumptions about the particular use case of this verifier, so we do NOT verify the following:
                ///         - Does NOT verify that the origin in the `clientDataJSON` matches the Relying Party's origin: tt is considered
                ///           the authenticator's responsibility to ensure that the user is interacting with the correct RP. This is
                ///           enforced by most high quality authenticators properly, particularly the iCloud Keychain and Google Password
                ///           Manager were tested.
                ///         - Does NOT verify That `topOrigin` in `clientDataJSON` is well-formed: We assume it would never be present, i.e.
                ///           the credentials are never used in a cross-origin/iframe context. The website/app set up should disallow
                ///           cross-origin usage of the credentials. This is the default behaviour for created credentials in common settings.
                ///         - Does NOT verify that the `rpIdHash` in `authenticatorData` is the SHA-256 hash of the RP ID expected by the Relying
                ///           Party: this means that we rely on the authenticator to properly enforce credentials to be used only by the correct RP.
                ///           This is generally enforced with features like Apple App Site Association and Google Asset Links. To protect from
                ///           edge cases in which a previously-linked RP ID is removed from the authorised RP IDs, we recommend that messages
                ///           signed by the authenticator include some expiry mechanism.
                ///         - Does NOT verify the credential backup state: this assumes the credential backup state is NOT used as part of Relying
                ///           Party business logic or policy.
                ///         - Does NOT verify the values of the client extension outputs: this assumes that the Relying Party does not use client
                ///           extension outputs.
                ///         - Does NOT verify the signature counter: signature counters are intended to enable risk scoring for the Relying Party.
                ///           This assumes risk scoring is not used as part of Relying Party business logic or policy.
                ///         - Does NOT verify the attestation object: this assumes that response.attestationObject is NOT present in the response,
                ///           i.e. the RP does not intend to verify an attestation.
                ///
                /// @param challenge    The challenge that was provided by the relying party.
                /// @param requireUV    A boolean indicating whether user verification is required.
                /// @param webAuthnAuth The `WebAuthnAuth` struct.
                /// @param x            The x coordinate of the public key.
                /// @param y            The y coordinate of the public key.
                ///
                /// @return `true` if the authentication assertion passed validation, else `false`.
                function verify(bytes memory challenge, bool requireUV, WebAuthnAuth memory webAuthnAuth, uint256 x, uint256 y)
                    internal
                    view
                    returns (bool)
                {
                    if (webAuthnAuth.s > _P256_N_DIV_2) {
                        // guard against signature malleability
                        return false;
                    }
                    // 11. Verify that the value of C.type is the string webauthn.get.
                    //     bytes("type":"webauthn.get").length = 21
                    string memory _type = webAuthnAuth.clientDataJSON.slice(webAuthnAuth.typeIndex, webAuthnAuth.typeIndex + 21);
                    if (keccak256(bytes(_type)) != _EXPECTED_TYPE_HASH) {
                        return false;
                    }
                    // 12. Verify that the value of C.challenge equals the base64url encoding of options.challenge.
                    bytes memory expectedChallenge = bytes(string.concat('"challenge":"', Base64.encodeURL(challenge), '"'));
                    string memory actualChallenge =
                        webAuthnAuth.clientDataJSON.slice(webAuthnAuth.challengeIndex, webAuthnAuth.challengeIndex + expectedChallenge.length);
                    if (keccak256(bytes(actualChallenge)) != keccak256(expectedChallenge)) {
                        return false;
                    }
                    // Skip 13., 14., 15.
                    // 16. Verify that the UP bit of the flags in authData is set.
                    if (webAuthnAuth.authenticatorData[32] & _AUTH_DATA_FLAGS_UP != _AUTH_DATA_FLAGS_UP) {
                        return false;
                    }
                    // 17. If user verification is required for this assertion, verify that the User Verified bit of the flags in
                    //     authData is set.
                    if (requireUV && (webAuthnAuth.authenticatorData[32] & _AUTH_DATA_FLAGS_UV) != _AUTH_DATA_FLAGS_UV) {
                        return false;
                    }
                    // skip 18.
                    // 19. Let hash be the result of computing a hash over the cData using SHA-256.
                    bytes32 clientDataJSONHash = sha256(bytes(webAuthnAuth.clientDataJSON));
                    // 20. Using credentialPublicKey, verify that sig is a valid signature over the binary concatenation of authData
                    //     and hash.
                    bytes32 messageHash = sha256(abi.encodePacked(webAuthnAuth.authenticatorData, clientDataJSONHash));
                    bytes memory args = abi.encode(messageHash, webAuthnAuth.r, webAuthnAuth.s, x, y);
                    // try the RIP-7212 precompile address
                    (bool success, bytes memory ret) = _VERIFIER.staticcall(args);
                    // staticcall will not revert if address has no code
                    // check return length
                    // note that even if precompile exists, ret.length is 0 when verification returns false
                    // so an invalid signature will be checked twice: once by the precompile and once by FCL.
                    // Ideally this signature failure is simulated offchain and no one actually pay this gas.
                    bool valid = ret.length > 0;
                    if (success && valid) return abi.decode(ret, (uint256)) == 1;
                    return FCL_ecdsa.ecdsa_verify(messageHash, webAuthnAuth.r, webAuthnAuth.s, x, y);
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.4;
            /// @title ERC-1271
            ///
            /// @notice Abstract ERC-1271 implementation (based on Solady's) with guards to handle the same
            ///         signer being used on multiple accounts.
            ///
            /// @dev To prevent the same signature from being validated on different accounts owned by the samer signer,
            ///      we introduce an anti cross-account-replay layer: the original hash is input into a new EIP-712 compliant
            ///      hash. The domain separator of this outer hash contains the chain id and address of this contract, so that
            ///      it cannot be used on two accounts (see `replaySafeHash()` for the implementation details).
            ///
            /// @author Coinbase (https://github.com/coinbase/smart-wallet)
            /// @author Solady (https://github.com/vectorized/solady/blob/main/src/accounts/ERC1271.sol)
            abstract contract ERC1271 {
                /// @dev Precomputed `typeHash` used to produce EIP-712 compliant hash when applying the anti
                ///      cross-account-replay layer.
                ///
                ///      The original hash must either be:
                ///         - An EIP-191 hash: keccak256("\\x19Ethereum Signed Message:\
            " || len(someMessage) || someMessage)
                ///         - An EIP-712 hash: keccak256("\\x19\\x01" || someDomainSeparator || hashStruct(someStruct))
                bytes32 private constant _MESSAGE_TYPEHASH = keccak256("CoinbaseSmartWalletMessage(bytes32 hash)");
                /// @notice Returns information about the `EIP712Domain` used to create EIP-712 compliant hashes.
                ///
                /// @dev Follows ERC-5267 (see https://eips.ethereum.org/EIPS/eip-5267).
                ///
                /// @return fields The bitmap of used fields.
                /// @return name The value of the `EIP712Domain.name` field.
                /// @return version The value of the `EIP712Domain.version` field.
                /// @return chainId The value of the `EIP712Domain.chainId` field.
                /// @return verifyingContract The value of the `EIP712Domain.verifyingContract` field.
                /// @return salt The value of the `EIP712Domain.salt` field.
                /// @return extensions The list of EIP numbers, that extends EIP-712 with new domain fields.
                function eip712Domain()
                    external
                    view
                    virtual
                    returns (
                        bytes1 fields,
                        string memory name,
                        string memory version,
                        uint256 chainId,
                        address verifyingContract,
                        bytes32 salt,
                        uint256[] memory extensions
                    )
                {
                    fields = hex"0f"; // `0b1111`.
                    (name, version) = _domainNameAndVersion();
                    chainId = block.chainid;
                    verifyingContract = address(this);
                    salt = salt; // `bytes32(0)`.
                    extensions = extensions; // `new uint256[](0)`.
                }
                /// @notice Validates the `signature` against the given `hash`.
                ///
                /// @dev This implementation follows ERC-1271. See https://eips.ethereum.org/EIPS/eip-1271.
                /// @dev IMPORTANT: Signature verification is performed on the hash produced AFTER applying the anti
                ///      cross-account-replay layer on the given `hash` (i.e., verification is run on the replay-safe
                ///      hash version).
                ///
                /// @param hash      The original hash.
                /// @param signature The signature of the replay-safe hash to validate.
                ///
                /// @return result `0x1626ba7e` if validation succeeded, else `0xffffffff`.
                function isValidSignature(bytes32 hash, bytes calldata signature) public view virtual returns (bytes4 result) {
                    if (_isValidSignature({hash: replaySafeHash(hash), signature: signature})) {
                        // bytes4(keccak256("isValidSignature(bytes32,bytes)"))
                        return 0x1626ba7e;
                    }
                    return 0xffffffff;
                }
                /// @notice Wrapper around `_eip712Hash()` to produce a replay-safe hash fron the given `hash`.
                ///
                /// @dev The returned EIP-712 compliant replay-safe hash is the result of:
                ///      keccak256(
                ///         \\x19\\x01 ||
                ///         this.domainSeparator ||
                ///         hashStruct(CoinbaseSmartWalletMessage({ hash: `hash`}))
                ///      )
                ///
                /// @param hash The original hash.
                ///
                /// @return The corresponding replay-safe hash.
                function replaySafeHash(bytes32 hash) public view virtual returns (bytes32) {
                    return _eip712Hash(hash);
                }
                /// @notice Returns the `domainSeparator` used to create EIP-712 compliant hashes.
                ///
                /// @dev Implements domainSeparator = hashStruct(eip712Domain).
                ///      See https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator.
                ///
                /// @return The 32 bytes domain separator result.
                function domainSeparator() public view returns (bytes32) {
                    (string memory name, string memory version) = _domainNameAndVersion();
                    return keccak256(
                        abi.encode(
                            keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                            keccak256(bytes(name)),
                            keccak256(bytes(version)),
                            block.chainid,
                            address(this)
                        )
                    );
                }
                /// @notice Returns the EIP-712 typed hash of the `CoinbaseSmartWalletMessage(bytes32 hash)` data structure.
                ///
                /// @dev Implements encode(domainSeparator : ?²⁵⁶, message : ?) = "\\x19\\x01" || domainSeparator ||
                ///      hashStruct(message).
                /// @dev See https://eips.ethereum.org/EIPS/eip-712#specification.
                ///
                /// @param hash The `CoinbaseSmartWalletMessage.hash` field to hash.
                ////
                /// @return The resulting EIP-712 hash.
                function _eip712Hash(bytes32 hash) internal view virtual returns (bytes32) {
                    return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator(), _hashStruct(hash)));
                }
                /// @notice Returns the EIP-712 `hashStruct` result of the `CoinbaseSmartWalletMessage(bytes32 hash)` data
                ///         structure.
                ///
                /// @dev Implements hashStruct(s : ?) = keccak256(typeHash || encodeData(s)).
                /// @dev See https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
                ///
                /// @param hash The `CoinbaseSmartWalletMessage.hash` field.
                ///
                /// @return The EIP-712 `hashStruct` result.
                function _hashStruct(bytes32 hash) internal view virtual returns (bytes32) {
                    return keccak256(abi.encode(_MESSAGE_TYPEHASH, hash));
                }
                /// @notice Returns the domain name and version to use when creating EIP-712 signatures.
                ///
                /// @dev MUST be defined by the implementation.
                ///
                /// @return name    The user readable name of signing domain.
                /// @return version The current major version of the signing domain.
                function _domainNameAndVersion() internal view virtual returns (string memory name, string memory version);
                /// @notice Validates the `signature` against the given `hash`.
                ///
                /// @dev MUST be defined by the implementation.
                ///
                /// @param hash      The hash whose signature has been performed on.
                /// @param signature The signature associated with `hash`.
                ///
                /// @return `true` is the signature is valid, else `false`.
                function _isValidSignature(bytes32 hash, bytes calldata signature) internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.18;
            /// @notice Storage layout used by this contract.
            ///
            /// @custom:storage-location erc7201:coinbase.storage.MultiOwnable
            struct MultiOwnableStorage {
                /// @dev Tracks the index of the next owner to add.
                uint256 nextOwnerIndex;
                /// @dev Tracks number of owners that have been removed.
                uint256 removedOwnersCount;
                /// @dev Maps index to owner bytes, used to idenfitied owners via a uint256 index.
                ///
                ///      Some uses—-such as signature validation for secp256r1 public key owners—-
                ///      requires the caller to assert the public key of the caller. To economize calldata,
                ///      we allow an index to identify an owner, so that the full owner bytes do
                ///      not need to be passed.
                ///
                ///      The `owner` bytes should either be
                ///         - An ABI encoded Ethereum address
                ///         - An ABI encoded public key
                mapping(uint256 index => bytes owner) ownerAtIndex;
                /// @dev Mapping of bytes to booleans indicating whether or not
                ///      bytes_ is an owner of this contract.
                mapping(bytes bytes_ => bool isOwner_) isOwner;
            }
            /// @title Multi Ownable
            ///
            /// @notice Auth contract allowing multiple owners, each identified as bytes.
            ///
            /// @author Coinbase (https://github.com/coinbase/smart-wallet)
            contract MultiOwnable {
                /// @dev Slot for the `MultiOwnableStorage` struct in storage.
                ///      Computed from
                ///      keccak256(abi.encode(uint256(keccak256("coinbase.storage.MultiOwnable")) - 1)) & ~bytes32(uint256(0xff))
                ///      Follows ERC-7201 (see https://eips.ethereum.org/EIPS/eip-7201).
                bytes32 private constant MUTLI_OWNABLE_STORAGE_LOCATION =
                    0x97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f00;
                /// @notice Thrown when the `msg.sender` is not an owner and is trying to call a privileged function.
                error Unauthorized();
                /// @notice Thrown when trying to add an already registered owner.
                ///
                /// @param owner The owner bytes.
                error AlreadyOwner(bytes owner);
                /// @notice Thrown when trying to remove an owner from an index that is empty.
                ///
                /// @param index The targeted index for removal.
                error NoOwnerAtIndex(uint256 index);
                /// @notice Thrown when `owner` argument does not match owner found at index.
                ///
                /// @param index         The index of the owner to be removed.
                /// @param expectedOwner The owner passed in the remove call.
                /// @param actualOwner   The actual owner at `index`.
                error WrongOwnerAtIndex(uint256 index, bytes expectedOwner, bytes actualOwner);
                /// @notice Thrown when a provided owner is neither 64 bytes long (for public key)
                ///         nor a ABI encoded address.
                ///
                /// @param owner The invalid owner.
                error InvalidOwnerBytesLength(bytes owner);
                /// @notice Thrown if a provided owner is 32 bytes long but does not fit in an `address` type.
                ///
                /// @param owner The invalid owner.
                error InvalidEthereumAddressOwner(bytes owner);
                /// @notice Thrown when removeOwnerAtIndex is called and there is only one current owner.
                error LastOwner();
                /// @notice Thrown when removeLastOwner is called and there is more than one current owner.
                ///
                /// @param ownersRemaining The number of current owners.
                error NotLastOwner(uint256 ownersRemaining);
                /// @notice Emitted when a new owner is registered.
                ///
                /// @param index The owner index of the owner added.
                /// @param owner The owner added.
                event AddOwner(uint256 indexed index, bytes owner);
                /// @notice Emitted when an owner is removed.
                ///
                /// @param index The owner index of the owner removed.
                /// @param owner The owner removed.
                event RemoveOwner(uint256 indexed index, bytes owner);
                /// @notice Access control modifier ensuring the caller is an authorized owner
                modifier onlyOwner() virtual {
                    _checkOwner();
                    _;
                }
                /// @notice Adds a new Ethereum-address owner.
                ///
                /// @param owner The owner address.
                function addOwnerAddress(address owner) external virtual onlyOwner {
                    _addOwnerAtIndex(abi.encode(owner), _getMultiOwnableStorage().nextOwnerIndex++);
                }
                /// @notice Adds a new public-key owner.
                ///
                /// @param x The owner public key x coordinate.
                /// @param y The owner public key y coordinate.
                function addOwnerPublicKey(bytes32 x, bytes32 y) external virtual onlyOwner {
                    _addOwnerAtIndex(abi.encode(x, y), _getMultiOwnableStorage().nextOwnerIndex++);
                }
                /// @notice Removes owner at the given `index`.
                ///
                /// @dev Reverts if the owner is not registered at `index`.
                /// @dev Reverts if there is currently only one owner.
                /// @dev Reverts if `owner` does not match bytes found at `index`.
                ///
                /// @param index The index of the owner to be removed.
                /// @param owner The ABI encoded bytes of the owner to be removed.
                function removeOwnerAtIndex(uint256 index, bytes calldata owner) external virtual onlyOwner {
                    if (ownerCount() == 1) {
                        revert LastOwner();
                    }
                    _removeOwnerAtIndex(index, owner);
                }
                /// @notice Removes owner at the given `index`, which should be the only current owner.
                ///
                /// @dev Reverts if the owner is not registered at `index`.
                /// @dev Reverts if there is currently more than one owner.
                /// @dev Reverts if `owner` does not match bytes found at `index`.
                ///
                /// @param index The index of the owner to be removed.
                /// @param owner The ABI encoded bytes of the owner to be removed.
                function removeLastOwner(uint256 index, bytes calldata owner) external virtual onlyOwner {
                    uint256 ownersRemaining = ownerCount();
                    if (ownersRemaining > 1) {
                        revert NotLastOwner(ownersRemaining);
                    }
                    _removeOwnerAtIndex(index, owner);
                }
                /// @notice Checks if the given `account` address is registered as owner.
                ///
                /// @param account The account address to check.
                ///
                /// @return `true` if the account is an owner else `false`.
                function isOwnerAddress(address account) public view virtual returns (bool) {
                    return _getMultiOwnableStorage().isOwner[abi.encode(account)];
                }
                /// @notice Checks if the given `x`, `y` public key is registered as owner.
                ///
                /// @param x The public key x coordinate.
                /// @param y The public key y coordinate.
                ///
                /// @return `true` if the account is an owner else `false`.
                function isOwnerPublicKey(bytes32 x, bytes32 y) public view virtual returns (bool) {
                    return _getMultiOwnableStorage().isOwner[abi.encode(x, y)];
                }
                /// @notice Checks if the given `account` bytes is registered as owner.
                ///
                /// @param account The account, should be ABI encoded address or public key.
                ///
                /// @return `true` if the account is an owner else `false`.
                function isOwnerBytes(bytes memory account) public view virtual returns (bool) {
                    return _getMultiOwnableStorage().isOwner[account];
                }
                /// @notice Returns the owner bytes at the given `index`.
                ///
                /// @param index The index to lookup.
                ///
                /// @return The owner bytes (empty if no owner is registered at this `index`).
                function ownerAtIndex(uint256 index) public view virtual returns (bytes memory) {
                    return _getMultiOwnableStorage().ownerAtIndex[index];
                }
                /// @notice Returns the next index that will be used to add a new owner.
                ///
                /// @return The next index that will be used to add a new owner.
                function nextOwnerIndex() public view virtual returns (uint256) {
                    return _getMultiOwnableStorage().nextOwnerIndex;
                }
                /// @notice Returns the current number of owners
                ///
                /// @return The current owner count
                function ownerCount() public view virtual returns (uint256) {
                    MultiOwnableStorage storage $ = _getMultiOwnableStorage();
                    return $.nextOwnerIndex - $.removedOwnersCount;
                }
                /// @notice Tracks the number of owners removed
                ///
                /// @dev Used with `this.nextOwnerIndex` to avoid removing all owners
                ///
                /// @return The number of owners that have been removed.
                function removedOwnersCount() public view virtual returns (uint256) {
                    return _getMultiOwnableStorage().removedOwnersCount;
                }
                /// @notice Initialize the owners of this contract.
                ///
                /// @dev Intended to be called contract is first deployed and never again.
                /// @dev Reverts if a provided owner is neither 64 bytes long (for public key) nor a valid address.
                ///
                /// @param owners The initial set of owners.
                function _initializeOwners(bytes[] memory owners) internal virtual {
                    MultiOwnableStorage storage $ = _getMultiOwnableStorage();
                    uint256 nextOwnerIndex_ = $.nextOwnerIndex;
                    for (uint256 i; i < owners.length; i++) {
                        if (owners[i].length != 32 && owners[i].length != 64) {
                            revert InvalidOwnerBytesLength(owners[i]);
                        }
                        if (owners[i].length == 32 && uint256(bytes32(owners[i])) > type(uint160).max) {
                            revert InvalidEthereumAddressOwner(owners[i]);
                        }
                        _addOwnerAtIndex(owners[i], nextOwnerIndex_++);
                    }
                    $.nextOwnerIndex = nextOwnerIndex_;
                }
                /// @notice Adds an owner at the given `index`.
                ///
                /// @dev Reverts if `owner` is already registered as an owner.
                ///
                /// @param owner The owner raw bytes to register.
                /// @param index The index to write to.
                function _addOwnerAtIndex(bytes memory owner, uint256 index) internal virtual {
                    if (isOwnerBytes(owner)) revert AlreadyOwner(owner);
                    MultiOwnableStorage storage $ = _getMultiOwnableStorage();
                    $.isOwner[owner] = true;
                    $.ownerAtIndex[index] = owner;
                    emit AddOwner(index, owner);
                }
                /// @notice Removes owner at the given `index`.
                ///
                /// @dev Reverts if the owner is not registered at `index`.
                /// @dev Reverts if `owner` does not match bytes found at `index`.
                ///
                /// @param index The index of the owner to be removed.
                /// @param owner The ABI encoded bytes of the owner to be removed.
                function _removeOwnerAtIndex(uint256 index, bytes calldata owner) internal virtual {
                    bytes memory owner_ = ownerAtIndex(index);
                    if (owner_.length == 0) revert NoOwnerAtIndex(index);
                    if (keccak256(owner_) != keccak256(owner)) {
                        revert WrongOwnerAtIndex({index: index, expectedOwner: owner, actualOwner: owner_});
                    }
                    MultiOwnableStorage storage $ = _getMultiOwnableStorage();
                    delete $.isOwner[owner];
                    delete $.ownerAtIndex[index];
                    $.removedOwnersCount++;
                    emit RemoveOwner(index, owner);
                }
                /// @notice Checks if the sender is an owner of this contract or the contract itself.
                ///
                /// @dev Revert if the sender is not an owner fo the contract itself.
                function _checkOwner() internal view virtual {
                    if (isOwnerAddress(msg.sender) || (msg.sender == address(this))) {
                        return;
                    }
                    revert Unauthorized();
                }
                /// @notice Helper function to get a storage reference to the `MultiOwnableStorage` struct.
                ///
                /// @return $ A storage reference to the `MultiOwnableStorage` struct.
                function _getMultiOwnableStorage() internal pure returns (MultiOwnableStorage storage $) {
                    assembly ("memory-safe") {
                        $.slot := MUTLI_OWNABLE_STORAGE_LOCATION
                    }
                }
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.8.12;
            /* solhint-disable no-inline-assembly */
            /**
             * returned data from validateUserOp.
             * validateUserOp returns a uint256, with is created by `_packedValidationData` and parsed by `_parseValidationData`
             * @param aggregator - address(0) - the account validated the signature by itself.
             *              address(1) - the account failed to validate the signature.
             *              otherwise - this is an address of a signature aggregator that must be used to validate the signature.
             * @param validAfter - this UserOp is valid only after this timestamp.
             * @param validaUntil - this UserOp is valid only up to this timestamp.
             */
                struct ValidationData {
                    address aggregator;
                    uint48 validAfter;
                    uint48 validUntil;
                }
            //extract sigFailed, validAfter, validUntil.
            // also convert zero validUntil to type(uint48).max
                function _parseValidationData(uint validationData) pure returns (ValidationData memory data) {
                    address aggregator = address(uint160(validationData));
                    uint48 validUntil = uint48(validationData >> 160);
                    if (validUntil == 0) {
                        validUntil = type(uint48).max;
                    }
                    uint48 validAfter = uint48(validationData >> (48 + 160));
                    return ValidationData(aggregator, validAfter, validUntil);
                }
            // intersect account and paymaster ranges.
                function _intersectTimeRange(uint256 validationData, uint256 paymasterValidationData) pure returns (ValidationData memory) {
                    ValidationData memory accountValidationData = _parseValidationData(validationData);
                    ValidationData memory pmValidationData = _parseValidationData(paymasterValidationData);
                    address aggregator = accountValidationData.aggregator;
                    if (aggregator == address(0)) {
                        aggregator = pmValidationData.aggregator;
                    }
                    uint48 validAfter = accountValidationData.validAfter;
                    uint48 validUntil = accountValidationData.validUntil;
                    uint48 pmValidAfter = pmValidationData.validAfter;
                    uint48 pmValidUntil = pmValidationData.validUntil;
                    if (validAfter < pmValidAfter) validAfter = pmValidAfter;
                    if (validUntil > pmValidUntil) validUntil = pmValidUntil;
                    return ValidationData(aggregator, validAfter, validUntil);
                }
            /**
             * helper to pack the return value for validateUserOp
             * @param data - the ValidationData to pack
             */
                function _packValidationData(ValidationData memory data) pure returns (uint256) {
                    return uint160(data.aggregator) | (uint256(data.validUntil) << 160) | (uint256(data.validAfter) << (160 + 48));
                }
            /**
             * helper to pack the return value for validateUserOp, when not using an aggregator
             * @param sigFailed - true for signature failure, false for success
             * @param validUntil last timestamp this UserOperation is valid (or zero for infinite)
             * @param validAfter first timestamp this UserOperation is valid
             */
                function _packValidationData(bool sigFailed, uint48 validUntil, uint48 validAfter) pure returns (uint256) {
                    return (sigFailed ? 1 : 0) | (uint256(validUntil) << 160) | (uint256(validAfter) << (160 + 48));
                }
            /**
             * keccak function over calldata.
             * @dev copy calldata into memory, do keccak and drop allocated memory. Strangely, this is more efficient than letting solidity do it.
             */
                function calldataKeccak(bytes calldata data) pure returns (bytes32 ret) {
                    assembly {
                        let mem := mload(0x40)
                        let len := data.length
                        calldatacopy(mem, data.offset, len)
                        ret := keccak256(mem, len)
                    }
                }
            //********************************************************************************************/
            //  ___           _       ___               _         _    _ _
            // | __| _ ___ __| |_    / __|_ _ _  _ _ __| |_ ___  | |  (_) |__
            // | _| '_/ -_|_-< ' \\  | (__| '_| || | '_ \\  _/ _ \\ | |__| | '_ \\
            // |_||_| \\___/__/_||_|  \\___|_|  \\_, | .__/\\__\\___/ |____|_|_.__/
            //                                |__/|_|
            ///* Copyright (C) 2022 - Renaud Dubois - This file is part of FCL (Fresh CryptoLib) project
            ///* License: This software is licensed under MIT License
            ///* This Code may be reused including license and copyright notice.
            ///* See LICENSE file at the root folder of the project.
            ///* FILE: FCL_ecdsa.sol
            ///*
            ///*
            ///* DESCRIPTION: ecdsa verification implementation
            ///*
            //**************************************************************************************/
            //* WARNING: this code SHALL not be used for non prime order curves for security reasons.
            // Code is optimized for a=-3 only curves with prime order, constant like -1, -2 shall be replaced
            // if ever used for other curve than sec256R1
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.8.19 <0.9.0;
            import {FCL_Elliptic_ZZ} from "./FCL_elliptic.sol";
            library FCL_ecdsa {
                // Set parameters for curve sec256r1.public
                  //curve order (number of points)
                uint256 constant n = FCL_Elliptic_ZZ.n;
              
                /**
                 * @dev ECDSA verification, given , signature, and public key.
                 */
                /**
                 * @dev ECDSA verification, given , signature, and public key, no calldata version
                 */
                function ecdsa_verify(bytes32 message, uint256 r, uint256 s, uint256 Qx, uint256 Qy)  internal view returns (bool){
                    if (r == 0 || r >= FCL_Elliptic_ZZ.n || s == 0 || s >= FCL_Elliptic_ZZ.n) {
                        return false;
                    }
                    
                    if (!FCL_Elliptic_ZZ.ecAff_isOnCurve(Qx, Qy)) {
                        return false;
                    }
                    uint256 sInv = FCL_Elliptic_ZZ.FCL_nModInv(s);
                    uint256 scalar_u = mulmod(uint256(message), sInv, FCL_Elliptic_ZZ.n);
                    uint256 scalar_v = mulmod(r, sInv, FCL_Elliptic_ZZ.n);
                    uint256 x1;
                    x1 = FCL_Elliptic_ZZ.ecZZ_mulmuladd_S_asm(Qx, Qy, scalar_u, scalar_v);
                    x1= addmod(x1, n-r,n );
                
                    return x1 == 0;
                }
                function ec_recover_r1(uint256 h, uint256 v, uint256 r, uint256 s) internal view returns (address)
                {
                     if (r == 0 || r >= FCL_Elliptic_ZZ.n || s == 0 || s >= FCL_Elliptic_ZZ.n) {
                        return address(0);
                    }
                    uint256 y=FCL_Elliptic_ZZ.ec_Decompress(r, v-27);
                    uint256 rinv=FCL_Elliptic_ZZ.FCL_nModInv(r);
                    uint256 u1=mulmod(FCL_Elliptic_ZZ.n-addmod(0,h,FCL_Elliptic_ZZ.n), rinv,FCL_Elliptic_ZZ.n);//-hr^-1
                    uint256 u2=mulmod(s, rinv,FCL_Elliptic_ZZ.n);//sr^-1
                    uint256 Qx;
                    uint256 Qy;
                    (Qx,Qy)=FCL_Elliptic_ZZ.ecZZ_mulmuladd(r,y, u1, u2);
                    return address(uint160(uint256(keccak256(abi.encodePacked(Qx, Qy)))));
                }
                function ecdsa_precomputed_verify(bytes32 message, uint256 r, uint256 s, address Shamir8)
                    internal view
                    returns (bool)
                {
                   
                    if (r == 0 || r >= n || s == 0 || s >= n) {
                        return false;
                    }
                    /* Q is pushed via the contract at address Shamir8 assumed to be correct
                    if (!isOnCurve(Q[0], Q[1])) {
                        return false;
                    }*/
                    uint256 sInv = FCL_Elliptic_ZZ.FCL_nModInv(s);
                    uint256 X;
                    //Shamir 8 dimensions
                    X = FCL_Elliptic_ZZ.ecZZ_mulmuladd_S8_extcode(mulmod(uint256(message), sInv, n), mulmod(r, sInv, n), Shamir8);
                    X= addmod(X, n-r,n );
                    return X == 0;
                } //end  ecdsa_precomputed_verify()
                 function ecdsa_precomputed_verify(bytes32 message, uint256[2] calldata rs, address Shamir8)
                    internal view
                    returns (bool)
                {
                    uint256 r = rs[0];
                    uint256 s = rs[1];
                    if (r == 0 || r >= n || s == 0 || s >= n) {
                        return false;
                    }
                    /* Q is pushed via the contract at address Shamir8 assumed to be correct
                    if (!isOnCurve(Q[0], Q[1])) {
                        return false;
                    }*/
                    uint256 sInv = FCL_Elliptic_ZZ.FCL_nModInv(s);
                    uint256 X;
                    //Shamir 8 dimensions
                    X = FCL_Elliptic_ZZ.ecZZ_mulmuladd_S8_extcode(mulmod(uint256(message), sInv, n), mulmod(r, sInv, n), Shamir8);
                    X= addmod(X, n-r,n );
                    return X == 0;
                } //end  ecdsa_precomputed_verify()
            }
            //********************************************************************************************/
            //  ___           _       ___               _         _    _ _
            // | __| _ ___ __| |_    / __|_ _ _  _ _ __| |_ ___  | |  (_) |__
            // | _| '_/ -_|_-< ' \\  | (__| '_| || | '_ \\  _/ _ \\ | |__| | '_ \\
            // |_||_| \\___/__/_||_|  \\___|_|  \\_, | .__/\\__\\___/ |____|_|_.__/
            //                                |__/|_|
            ///* Copyright (C) 2022 - Renaud Dubois - This file is part of FCL (Fresh CryptoLib) project
            ///* License: This software is licensed under MIT License
            ///* This Code may be reused including license and copyright notice.
            ///* See LICENSE file at the root folder of the project.
            ///* FILE: FCL_elliptic.sol
            ///*
            ///*
            ///* DESCRIPTION: modified XYZZ system coordinates for EVM elliptic point multiplication
            ///*  optimization
            ///*
            //**************************************************************************************/
            //* WARNING: this code SHALL not be used for non prime order curves for security reasons.
            // Code is optimized for a=-3 only curves with prime order, constant like -1, -2 shall be replaced
            // if ever used for other curve than sec256R1
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.8.19 <0.9.0;
            library FCL_Elliptic_ZZ {
                // Set parameters for curve sec256r1.
                // address of the ModExp precompiled contract (Arbitrary-precision exponentiation under modulo)
                address constant MODEXP_PRECOMPILE = 0x0000000000000000000000000000000000000005;
                //curve prime field modulus
                uint256 constant p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF;
                //short weierstrass first coefficient
                uint256 constant a = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC;
                //short weierstrass second coefficient
                uint256 constant b = 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B;
                //generating point affine coordinates
                uint256 constant gx = 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296;
                uint256 constant gy = 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5;
                //curve order (number of points)
                uint256 constant n = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551;
                /* -2 mod p constant, used to speed up inversion and doubling (avoid negation)*/
                uint256 constant minus_2 = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFD;
                /* -2 mod n constant, used to speed up inversion*/
                uint256 constant minus_2modn = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254F;
                uint256 constant minus_1 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
                //P+1 div 4
                uint256 constant pp1div4=0x3fffffffc0000000400000000000000000000000400000000000000000000000;
                //arbitrary constant to express no quadratic residuosity
                uint256 constant _NOTSQUARE=0xFFFFFFFF00000002000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF;
                uint256 constant _NOTONCURVE=0xFFFFFFFF00000003000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF;
                /**
                 * /* inversion mod n via a^(n-2), use of precompiled using little Fermat theorem
                 */
                function FCL_nModInv(uint256 u) internal view returns (uint256 result) {
                    assembly {
                        let pointer := mload(0x40)
                        // Define length of base, exponent and modulus. 0x20 == 32 bytes
                        mstore(pointer, 0x20)
                        mstore(add(pointer, 0x20), 0x20)
                        mstore(add(pointer, 0x40), 0x20)
                        // Define variables base, exponent and modulus
                        mstore(add(pointer, 0x60), u)
                        mstore(add(pointer, 0x80), minus_2modn)
                        mstore(add(pointer, 0xa0), n)
                        // Call the precompiled contract 0x05 = ModExp
                        if iszero(staticcall(not(0), 0x05, pointer, 0xc0, pointer, 0x20)) { revert(0, 0) }
                        result := mload(pointer)
                    }
                }
                /**
                 * /* @dev inversion mod nusing little Fermat theorem via a^(n-2), use of precompiled
                 */
                function FCL_pModInv(uint256 u) internal view returns (uint256 result) {
                    assembly {
                        let pointer := mload(0x40)
                        // Define length of base, exponent and modulus. 0x20 == 32 bytes
                        mstore(pointer, 0x20)
                        mstore(add(pointer, 0x20), 0x20)
                        mstore(add(pointer, 0x40), 0x20)
                        // Define variables base, exponent and modulus
                        mstore(add(pointer, 0x60), u)
                        mstore(add(pointer, 0x80), minus_2)
                        mstore(add(pointer, 0xa0), p)
                        // Call the precompiled contract 0x05 = ModExp
                        if iszero(staticcall(not(0), 0x05, pointer, 0xc0, pointer, 0x20)) { revert(0, 0) }
                        result := mload(pointer)
                    }
                }
                //Coron projective shuffling, take as input alpha as blinding factor
               function ecZZ_Coronize(uint256 alpha, uint256 x, uint256 y,  uint256 zz, uint256 zzz) internal pure  returns (uint256 x3, uint256 y3, uint256 zz3, uint256 zzz3)
               {
                   
                    uint256 alpha2=mulmod(alpha,alpha,p);
                   
                    x3=mulmod(alpha2, x,p); //alpha^-2.x
                    y3=mulmod(mulmod(alpha, alpha2,p), y,p);
                    zz3=mulmod(zz,alpha2,p);//alpha^2 zz
                    zzz3=mulmod(zzz,mulmod(alpha, alpha2,p),p);//alpha^3 zzz
                    
                    return (x3, y3, zz3, zzz3);
               }
             function ecZZ_Add(uint256 x1, uint256 y1, uint256 zz1, uint256 zzz1, uint256 x2, uint256 y2, uint256 zz2, uint256 zzz2) internal pure  returns (uint256 x3, uint256 y3, uint256 zz3, uint256 zzz3)
              {
                uint256 u1=mulmod(x1,zz2,p); // U1 = X1*ZZ2
                uint256 u2=mulmod(x2, zz1,p);               //  U2 = X2*ZZ1
                u2=addmod(u2, p-u1, p);//  P = U2-U1
                x1=mulmod(u2, u2, p);//PP
                x2=mulmod(x1, u2, p);//PPP
                
                zz3=mulmod(x1, mulmod(zz1, zz2, p),p);//ZZ3 = ZZ1*ZZ2*PP  
                zzz3=mulmod(zzz1, mulmod(zzz2, x2, p),p);//ZZZ3 = ZZZ1*ZZZ2*PPP
                zz1=mulmod(y1, zzz2,p);  // S1 = Y1*ZZZ2
                zz2=mulmod(y2, zzz1, p);    // S2 = Y2*ZZZ1 
                zz2=addmod(zz2, p-zz1, p);//R = S2-S1
                zzz1=mulmod(u1, x1,p); //Q = U1*PP
                x3= addmod(addmod(mulmod(zz2, zz2, p), p-x2,p), mulmod(minus_2, zzz1,p),p); //X3 = R2-PPP-2*Q
                y3=addmod( mulmod(zz2, addmod(zzz1, p-x3, p),p), p-mulmod(zz1, x2, p),p);//R*(Q-X3)-S1*PPP
                return (x3, y3, zz3, zzz3);
              }
            /// @notice Calculate one modular square root of a given integer. Assume that p=3 mod 4.
            /// @dev Uses the ModExp precompiled contract at address 0x05 for fast computation using little Fermat theorem
            /// @param self The integer of which to find the modular inverse
            /// @return result The modular inverse of the input integer. If the modular inverse doesn't exist, it revert the tx
            function SqrtMod(uint256 self) internal view returns (uint256 result){
             assembly ("memory-safe") {
                    // load the free memory pointer value
                    let pointer := mload(0x40)
                    // Define length of base (Bsize)
                    mstore(pointer, 0x20)
                    // Define the exponent size (Esize)
                    mstore(add(pointer, 0x20), 0x20)
                    // Define the modulus size (Msize)
                    mstore(add(pointer, 0x40), 0x20)
                    // Define variables base (B)
                    mstore(add(pointer, 0x60), self)
                    // Define the exponent (E)
                    mstore(add(pointer, 0x80), pp1div4)
                    // We save the point of the last argument, it will be override by the result
                    // of the precompile call in order to avoid paying for the memory expansion properly
                    let _result := add(pointer, 0xa0)
                    // Define the modulus (M)
                    mstore(_result, p)
                    // Call the precompiled ModExp (0x05) https://www.evm.codes/precompiled#0x05
                    if iszero(
                        staticcall(
                            not(0), // amount of gas to send
                            MODEXP_PRECOMPILE, // target
                            pointer, // argsOffset
                            0xc0, // argsSize (6 * 32 bytes)
                            _result, // retOffset (we override M to avoid paying for the memory expansion)
                            0x20 // retSize (32 bytes)
                        )
                    ) { revert(0, 0) }
              result := mload(_result)
            //  result :=addmod(result,0,p)
             }
               if(mulmod(result,result,p)!=self){
                 result=_NOTSQUARE;
               }
              
               return result;
            }
                /**
                 * /* @dev Convert from affine rep to XYZZ rep
                 */
                function ecAff_SetZZ(uint256 x0, uint256 y0) internal pure returns (uint256[4] memory P) {
                    unchecked {
                        P[2] = 1; //ZZ
                        P[3] = 1; //ZZZ
                        P[0] = x0;
                        P[1] = y0;
                    }
                }
                function ec_Decompress(uint256 x, uint256 parity) internal view returns(uint256 y){ 
                    uint256 y2=mulmod(x,mulmod(x,x,p),p);//x3
                    y2=addmod(b,addmod(y2,mulmod(x,a,p),p),p);//x3+ax+b
                    y=SqrtMod(y2);
                    if(y==_NOTSQUARE){
                       return _NOTONCURVE;
                    }
                    if((y&1)!=(parity&1)){
                        y=p-y;
                    }
                }
                /**
                 * /* @dev Convert from XYZZ rep to affine rep
                 */
                /*    https://hyperelliptic.org/EFD/g1p/auto-shortw-xyzz-3.html#addition-add-2008-s*/
                function ecZZ_SetAff(uint256 x, uint256 y, uint256 zz, uint256 zzz) internal view returns (uint256 x1, uint256 y1) {
                    uint256 zzzInv = FCL_pModInv(zzz); //1/zzz
                    y1 = mulmod(y, zzzInv, p); //Y/zzz
                    uint256 _b = mulmod(zz, zzzInv, p); //1/z
                    zzzInv = mulmod(_b, _b, p); //1/zz
                    x1 = mulmod(x, zzzInv, p); //X/zz
                }
                /**
                 * /* @dev Sutherland2008 doubling
                 */
                /* The "dbl-2008-s-1" doubling formulas */
                function ecZZ_Dbl(uint256 x, uint256 y, uint256 zz, uint256 zzz)
                    internal
                    pure
                    returns (uint256 P0, uint256 P1, uint256 P2, uint256 P3)
                {
                    unchecked {
                        assembly {
                            P0 := mulmod(2, y, p) //U = 2*Y1
                            P2 := mulmod(P0, P0, p) // V=U^2
                            P3 := mulmod(x, P2, p) // S = X1*V
                            P1 := mulmod(P0, P2, p) // W=UV
                            P2 := mulmod(P2, zz, p) //zz3=V*ZZ1
                            zz := mulmod(3, mulmod(addmod(x, sub(p, zz), p), addmod(x, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1)
                            P0 := addmod(mulmod(zz, zz, p), mulmod(minus_2, P3, p), p) //X3=M^2-2S
                            x := mulmod(zz, addmod(P3, sub(p, P0), p), p) //M(S-X3)
                            P3 := mulmod(P1, zzz, p) //zzz3=W*zzz1
                            P1 := addmod(x, sub(p, mulmod(P1, y, p)), p) //Y3= M(S-X3)-W*Y1
                        }
                    }
                    return (P0, P1, P2, P3);
                }
                /**
                 * @dev Sutherland2008 add a ZZ point with a normalized point and greedy formulae
                 * warning: assume that P1(x1,y1)!=P2(x2,y2), true in multiplication loop with prime order (cofactor 1)
                 */
                function ecZZ_AddN(uint256 x1, uint256 y1, uint256 zz1, uint256 zzz1, uint256 x2, uint256 y2)
                    internal
                    pure
                    returns (uint256 P0, uint256 P1, uint256 P2, uint256 P3)
                {
                    unchecked {
                        if (y1 == 0) {
                            return (x2, y2, 1, 1);
                        }
                        assembly {
                            y1 := sub(p, y1)
                            y2 := addmod(mulmod(y2, zzz1, p), y1, p)
                            x2 := addmod(mulmod(x2, zz1, p), sub(p, x1), p)
                            P0 := mulmod(x2, x2, p) //PP = P^2
                            P1 := mulmod(P0, x2, p) //PPP = P*PP
                            P2 := mulmod(zz1, P0, p) ////ZZ3 = ZZ1*PP
                            P3 := mulmod(zzz1, P1, p) ////ZZZ3 = ZZZ1*PPP
                            zz1 := mulmod(x1, P0, p) //Q = X1*PP
                            P0 := addmod(addmod(mulmod(y2, y2, p), sub(p, P1), p), mulmod(minus_2, zz1, p), p) //R^2-PPP-2*Q
                            P1 := addmod(mulmod(addmod(zz1, sub(p, P0), p), y2, p), mulmod(y1, P1, p), p) //R*(Q-X3)
                        }
                        //end assembly
                    } //end unchecked
                    return (P0, P1, P2, P3);
                }
                /**
                 * @dev Return the zero curve in XYZZ coordinates.
                 */
                function ecZZ_SetZero() internal pure returns (uint256 x, uint256 y, uint256 zz, uint256 zzz) {
                    return (0, 0, 0, 0);
                }
                /**
                 * @dev Check if point is the neutral of the curve
                 */
                // uint256 x0, uint256 y0, uint256 zz0, uint256 zzz0
                function ecZZ_IsZero(uint256, uint256 y0, uint256, uint256) internal pure returns (bool) {
                    return y0 == 0;
                }
                /**
                 * @dev Return the zero curve in affine coordinates. Compatible with the double formulae (no special case)
                 */
                function ecAff_SetZero() internal pure returns (uint256 x, uint256 y) {
                    return (0, 0);
                }
                /**
                 * @dev Check if the curve is the zero curve in affine rep.
                 */
                // uint256 x, uint256 y)
                function ecAff_IsZero(uint256, uint256 y) internal pure returns (bool flag) {
                    return (y == 0);
                }
                /**
                 * @dev Check if a point in affine coordinates is on the curve (reject Neutral that is indeed on the curve).
                 */
                function ecAff_isOnCurve(uint256 x, uint256 y) internal pure returns (bool) {
                    if (x >= p || y >= p || ((x == 0) && (y == 0))) {
                        return false;
                    }
                    unchecked {
                        uint256 LHS = mulmod(y, y, p); // y^2
                        uint256 RHS = addmod(mulmod(mulmod(x, x, p), x, p), mulmod(x, a, p), p); // x^3+ax
                        RHS = addmod(RHS, b, p); // x^3 + a*x + b
                        return LHS == RHS;
                    }
                }
                /**
                 * @dev Add two elliptic curve points in affine coordinates. Deal with P=Q
                 */
                function ecAff_add(uint256 x0, uint256 y0, uint256 x1, uint256 y1) internal view returns (uint256, uint256) {
                    uint256 zz0;
                    uint256 zzz0;
                    if (ecAff_IsZero(x0, y0)) return (x1, y1);
                    if (ecAff_IsZero(x1, y1)) return (x0, y0);
                    if((x0==x1)&&(y0==y1)) {
                        (x0, y0, zz0, zzz0) = ecZZ_Dbl(x0, y0,1,1);
                    }
                    else{
                        (x0, y0, zz0, zzz0) = ecZZ_AddN(x0, y0, 1, 1, x1, y1);
                    }
                    return ecZZ_SetAff(x0, y0, zz0, zzz0);
                }
                /**
                 * @dev Computation of uG+vQ using Strauss-Shamir's trick, G basepoint, Q public key
                 *       Returns only x for ECDSA use            
                 *      */
                function ecZZ_mulmuladd_S_asm(
                    uint256 Q0,
                    uint256 Q1, //affine rep for input point Q
                    uint256 scalar_u,
                    uint256 scalar_v
                ) internal view returns (uint256 X) {
                    uint256 zz;
                    uint256 zzz;
                    uint256 Y;
                    uint256 index = 255;
                    uint256 H0;
                    uint256 H1;
                    unchecked {
                        if (scalar_u == 0 && scalar_v == 0) return 0;
                        (H0, H1) = ecAff_add(gx, gy, Q0, Q1); 
                        if((H0==0)&&(H1==0))//handling Q=-G
                        {
                            scalar_u=addmod(scalar_u, n-scalar_v, n);
                            scalar_v=0;
                            if (scalar_u == 0 && scalar_v == 0) return 0;
                        }
                        assembly {
                            for { let T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1)) } eq(T4, 0) {
                                index := sub(index, 1)
                                T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1))
                            } {}
                            zz := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1))
                            if eq(zz, 1) {
                                X := gx
                                Y := gy
                            }
                            if eq(zz, 2) {
                                X := Q0
                                Y := Q1
                            }
                            if eq(zz, 3) {
                                X := H0
                                Y := H1
                            }
                            index := sub(index, 1)
                            zz := 1
                            zzz := 1
                            for {} gt(minus_1, index) { index := sub(index, 1) } {
                                // inlined EcZZ_Dbl
                                let T1 := mulmod(2, Y, p) //U = 2*Y1, y free
                                let T2 := mulmod(T1, T1, p) // V=U^2
                                let T3 := mulmod(X, T2, p) // S = X1*V
                                T1 := mulmod(T1, T2, p) // W=UV
                                let T4 := mulmod(3, mulmod(addmod(X, sub(p, zz), p), addmod(X, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1)
                                zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1
                                zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free
                                X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S
                                T2 := mulmod(T4, addmod(X, sub(p, T3), p), p) //-M(S-X3)=M(X3-S)
                                Y := addmod(mulmod(T1, Y, p), T2, p) //-Y3= W*Y1-M(S-X3), we replace Y by -Y to avoid a sub in ecAdd
                                {
                                    //value of dibit
                                    T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1))
                                    if iszero(T4) {
                                        Y := sub(p, Y) //restore the -Y inversion
                                        continue
                                    } // if T4!=0
                                    if eq(T4, 1) {
                                        T1 := gx
                                        T2 := gy
                                    }
                                    if eq(T4, 2) {
                                        T1 := Q0
                                        T2 := Q1
                                    }
                                    if eq(T4, 3) {
                                        T1 := H0
                                        T2 := H1
                                    }
                                    if iszero(zz) {
                                        X := T1
                                        Y := T2
                                        zz := 1
                                        zzz := 1
                                        continue
                                    }
                                    // inlined EcZZ_AddN
                                    //T3:=sub(p, Y)
                                    //T3:=Y
                                    let y2 := addmod(mulmod(T2, zzz, p), Y, p) //R
                                    T2 := addmod(mulmod(T1, zz, p), sub(p, X), p) //P
                                    //special extremely rare case accumulator where EcAdd is replaced by EcDbl, no need to optimize this
                                    //todo : construct edge vector case
                                    if iszero(y2) {
                                        if iszero(T2) {
                                            T1 := mulmod(minus_2, Y, p) //U = 2*Y1, y free
                                            T2 := mulmod(T1, T1, p) // V=U^2
                                            T3 := mulmod(X, T2, p) // S = X1*V
                                            T1 := mulmod(T1, T2, p) // W=UV
                                            y2 := mulmod(addmod(X, zz, p), addmod(X, sub(p, zz), p), p) //(X-ZZ)(X+ZZ)
                                            T4 := mulmod(3, y2, p) //M=3*(X-ZZ)(X+ZZ)
                                            zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1
                                            zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free
                                            X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S
                                            T2 := mulmod(T4, addmod(T3, sub(p, X), p), p) //M(S-X3)
                                            Y := addmod(T2, mulmod(T1, Y, p), p) //Y3= M(S-X3)-W*Y1
                                            continue
                                        }
                                    }
                                    T4 := mulmod(T2, T2, p) //PP
                                    let TT1 := mulmod(T4, T2, p) //PPP, this one could be spared, but adding this register spare gas
                                    zz := mulmod(zz, T4, p)
                                    zzz := mulmod(zzz, TT1, p) //zz3=V*ZZ1
                                    let TT2 := mulmod(X, T4, p)
                                    T4 := addmod(addmod(mulmod(y2, y2, p), sub(p, TT1), p), mulmod(minus_2, TT2, p), p)
                                    Y := addmod(mulmod(addmod(TT2, sub(p, T4), p), y2, p), mulmod(Y, TT1, p), p)
                                    X := T4
                                }
                            } //end loop
                            let T := mload(0x40)
                            mstore(add(T, 0x60), zz)
                            //(X,Y)=ecZZ_SetAff(X,Y,zz, zzz);
                            //T[0] = inverseModp_Hard(T[0], p); //1/zzz, inline modular inversion using precompile:
                            // Define length of base, exponent and modulus. 0x20 == 32 bytes
                            mstore(T, 0x20)
                            mstore(add(T, 0x20), 0x20)
                            mstore(add(T, 0x40), 0x20)
                            // Define variables base, exponent and modulus
                            //mstore(add(pointer, 0x60), u)
                            mstore(add(T, 0x80), minus_2)
                            mstore(add(T, 0xa0), p)
                            // Call the precompiled contract 0x05 = ModExp
                            if iszero(staticcall(not(0), 0x05, T, 0xc0, T, 0x20)) { revert(0, 0) }
                            //Y:=mulmod(Y,zzz,p)//Y/zzz
                            //zz :=mulmod(zz, mload(T),p) //1/z
                            //zz:= mulmod(zz,zz,p) //1/zz
                            X := mulmod(X, mload(T), p) //X/zz
                        } //end assembly
                    } //end unchecked
                    return X;
                }
                /**
                 * @dev Computation of uG+vQ using Strauss-Shamir's trick, G basepoint, Q public key
                 *       Returns affine representation of point (normalized)       
                 *      */
                function ecZZ_mulmuladd(
                    uint256 Q0,
                    uint256 Q1, //affine rep for input point Q
                    uint256 scalar_u,
                    uint256 scalar_v
                ) internal view returns (uint256 X, uint256 Y) {
                    uint256 zz;
                    uint256 zzz;
                    uint256 index = 255;
                    uint256[6] memory T;
                    uint256[2] memory H;
             
                    unchecked {
                        if (scalar_u == 0 && scalar_v == 0) return (0,0);
                        (H[0], H[1]) = ecAff_add(gx, gy, Q0, Q1); //will not work if Q=P, obvious forbidden private key
                        assembly {
                            for { let T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1)) } eq(T4, 0) {
                                index := sub(index, 1)
                                T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1))
                            } {}
                            zz := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1))
                            if eq(zz, 1) {
                                X := gx
                                Y := gy
                            }
                            if eq(zz, 2) {
                                X := Q0
                                Y := Q1
                            }
                            if eq(zz, 3) {
                                Y := mload(add(H,32))
                                X := mload(H)
                            }
                            index := sub(index, 1)
                            zz := 1
                            zzz := 1
                            for {} gt(minus_1, index) { index := sub(index, 1) } {
                                // inlined EcZZ_Dbl
                                let T1 := mulmod(2, Y, p) //U = 2*Y1, y free
                                let T2 := mulmod(T1, T1, p) // V=U^2
                                let T3 := mulmod(X, T2, p) // S = X1*V
                                T1 := mulmod(T1, T2, p) // W=UV
                                let T4 := mulmod(3, mulmod(addmod(X, sub(p, zz), p), addmod(X, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1)
                                zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1
                                zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free
                                X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S
                                T2 := mulmod(T4, addmod(X, sub(p, T3), p), p) //-M(S-X3)=M(X3-S)
                                Y := addmod(mulmod(T1, Y, p), T2, p) //-Y3= W*Y1-M(S-X3), we replace Y by -Y to avoid a sub in ecAdd
                                {
                                    //value of dibit
                                    T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1))
                                    if iszero(T4) {
                                        Y := sub(p, Y) //restore the -Y inversion
                                        continue
                                    } // if T4!=0
                                    if eq(T4, 1) {
                                        T1 := gx
                                        T2 := gy
                                    }
                                    if eq(T4, 2) {
                                        T1 := Q0
                                        T2 := Q1
                                    }
                                    if eq(T4, 3) {
                                        T1 := mload(H)
                                        T2 := mload(add(H,32))
                                    }
                                    if iszero(zz) {
                                        X := T1
                                        Y := T2
                                        zz := 1
                                        zzz := 1
                                        continue
                                    }
                                    // inlined EcZZ_AddN
                                    //T3:=sub(p, Y)
                                    //T3:=Y
                                    let y2 := addmod(mulmod(T2, zzz, p), Y, p) //R
                                    T2 := addmod(mulmod(T1, zz, p), sub(p, X), p) //P
                                    //special extremely rare case accumulator where EcAdd is replaced by EcDbl, no need to optimize this
                                    //todo : construct edge vector case
                                    if iszero(y2) {
                                        if iszero(T2) {
                                            T1 := mulmod(minus_2, Y, p) //U = 2*Y1, y free
                                            T2 := mulmod(T1, T1, p) // V=U^2
                                            T3 := mulmod(X, T2, p) // S = X1*V
                                            T1 := mulmod(T1, T2, p) // W=UV
                                            y2 := mulmod(addmod(X, zz, p), addmod(X, sub(p, zz), p), p) //(X-ZZ)(X+ZZ)
                                            T4 := mulmod(3, y2, p) //M=3*(X-ZZ)(X+ZZ)
                                            zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1
                                            zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free
                                            X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S
                                            T2 := mulmod(T4, addmod(T3, sub(p, X), p), p) //M(S-X3)
                                            Y := addmod(T2, mulmod(T1, Y, p), p) //Y3= M(S-X3)-W*Y1
                                            continue
                                        }
                                    }
                                    T4 := mulmod(T2, T2, p) //PP
                                    let TT1 := mulmod(T4, T2, p) //PPP, this one could be spared, but adding this register spare gas
                                    zz := mulmod(zz, T4, p)
                                    zzz := mulmod(zzz, TT1, p) //zz3=V*ZZ1
                                    let TT2 := mulmod(X, T4, p)
                                    T4 := addmod(addmod(mulmod(y2, y2, p), sub(p, TT1), p), mulmod(minus_2, TT2, p), p)
                                    Y := addmod(mulmod(addmod(TT2, sub(p, T4), p), y2, p), mulmod(Y, TT1, p), p)
                                    X := T4
                                }
                            } //end loop
                            mstore(add(T, 0x60), zzz)
                            //(X,Y)=ecZZ_SetAff(X,Y,zz, zzz);
                            //T[0] = inverseModp_Hard(T[0], p); //1/zzz, inline modular inversion using precompile:
                            // Define length of base, exponent and modulus. 0x20 == 32 bytes
                            mstore(T, 0x20)
                            mstore(add(T, 0x20), 0x20)
                            mstore(add(T, 0x40), 0x20)
                            // Define variables base, exponent and modulus
                            //mstore(add(pointer, 0x60), u)
                            mstore(add(T, 0x80), minus_2)
                            mstore(add(T, 0xa0), p)
                            // Call the precompiled contract 0x05 = ModExp
                            if iszero(staticcall(not(0), 0x05, T, 0xc0, T, 0x20)) { revert(0, 0) }
                            Y:=mulmod(Y,mload(T),p)//Y/zzz
                            zz :=mulmod(zz, mload(T),p) //1/z
                            zz:= mulmod(zz,zz,p) //1/zz
                            X := mulmod(X, zz, p) //X/zz
                        } //end assembly
                    } //end unchecked
                    return (X,Y);
                }
                //8 dimensions Shamir's trick, using precomputations stored in Shamir8,  stored as Bytecode of an external
                //contract at given address dataPointer
                //(thx to Lakhdar https://github.com/Kelvyne for EVM storage explanations and tricks)
                // the external tool to generate tables from public key is in the /sage directory
                function ecZZ_mulmuladd_S8_extcode(uint256 scalar_u, uint256 scalar_v, address dataPointer)
                    internal view
                    returns (uint256 X /*, uint Y*/ )
                {
                    unchecked {
                        uint256 zz; // third and  coordinates of the point
                        uint256[6] memory T;
                        zz = 256; //start index
                        while (T[0] == 0) {
                            zz = zz - 1;
                            //tbd case of msb octobit is null
                            T[0] = 64
                                * (
                                    128 * ((scalar_v >> zz) & 1) + 64 * ((scalar_v >> (zz - 64)) & 1)
                                        + 32 * ((scalar_v >> (zz - 128)) & 1) + 16 * ((scalar_v >> (zz - 192)) & 1)
                                        + 8 * ((scalar_u >> zz) & 1) + 4 * ((scalar_u >> (zz - 64)) & 1)
                                        + 2 * ((scalar_u >> (zz - 128)) & 1) + ((scalar_u >> (zz - 192)) & 1)
                                );
                        }
                        assembly {
                            extcodecopy(dataPointer, T, mload(T), 64)
                            let index := sub(zz, 1)
                            X := mload(T)
                            let Y := mload(add(T, 32))
                            let zzz := 1
                            zz := 1
                            //loop over 1/4 of scalars thx to Shamir's trick over 8 points
                            for {} gt(index, 191) { index := add(index, 191) } {
                                //inline Double
                                {
                                    let TT1 := mulmod(2, Y, p) //U = 2*Y1, y free
                                    let T2 := mulmod(TT1, TT1, p) // V=U^2
                                    let T3 := mulmod(X, T2, p) // S = X1*V
                                    let T1 := mulmod(TT1, T2, p) // W=UV
                                    let T4 := mulmod(3, mulmod(addmod(X, sub(p, zz), p), addmod(X, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1)
                                    zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1
                                    zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free
                                    X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S
                                    //T2:=mulmod(T4,addmod(T3, sub(p, X),p),p)//M(S-X3)
                                    let T5 := mulmod(T4, addmod(X, sub(p, T3), p), p) //-M(S-X3)=M(X3-S)
                                    //Y:= addmod(T2, sub(p, mulmod(T1, Y ,p)),p  )//Y3= M(S-X3)-W*Y1
                                    Y := addmod(mulmod(T1, Y, p), T5, p) //-Y3= W*Y1-M(S-X3), we replace Y by -Y to avoid a sub in ecAdd
                                    /* compute element to access in precomputed table */
                                }
                                {
                                    let T4 := add(shl(13, and(shr(index, scalar_v), 1)), shl(9, and(shr(index, scalar_u), 1)))
                                    let index2 := sub(index, 64)
                                    let T3 :=
                                        add(T4, add(shl(12, and(shr(index2, scalar_v), 1)), shl(8, and(shr(index2, scalar_u), 1))))
                                    let index3 := sub(index2, 64)
                                    let T2 :=
                                        add(T3, add(shl(11, and(shr(index3, scalar_v), 1)), shl(7, and(shr(index3, scalar_u), 1))))
                                    index := sub(index3, 64)
                                    let T1 :=
                                        add(T2, add(shl(10, and(shr(index, scalar_v), 1)), shl(6, and(shr(index, scalar_u), 1))))
                                    //tbd: check validity of formulae with (0,1) to remove conditional jump
                                    if iszero(T1) {
                                        Y := sub(p, Y)
                                        continue
                                    }
                                    extcodecopy(dataPointer, T, T1, 64)
                                }
                                {
                                    /* Access to precomputed table using extcodecopy hack */
                                    // inlined EcZZ_AddN
                                    if iszero(zz) {
                                        X := mload(T)
                                        Y := mload(add(T, 32))
                                        zz := 1
                                        zzz := 1
                                        continue
                                    }
                                    let y2 := addmod(mulmod(mload(add(T, 32)), zzz, p), Y, p)
                                    let T2 := addmod(mulmod(mload(T), zz, p), sub(p, X), p)
                                    //special case ecAdd(P,P)=EcDbl
                                    if iszero(y2) {
                                        if iszero(T2) {
                                            let T1 := mulmod(minus_2, Y, p) //U = 2*Y1, y free
                                            T2 := mulmod(T1, T1, p) // V=U^2
                                            let T3 := mulmod(X, T2, p) // S = X1*V
                                            T1 := mulmod(T1, T2, p) // W=UV
                                            y2 := mulmod(addmod(X, zz, p), addmod(X, sub(p, zz), p), p) //(X-ZZ)(X+ZZ)
                                            let T4 := mulmod(3, y2, p) //M=3*(X-ZZ)(X+ZZ)
                                            zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1
                                            zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free
                                            X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S
                                            T2 := mulmod(T4, addmod(T3, sub(p, X), p), p) //M(S-X3)
                                            Y := addmod(T2, mulmod(T1, Y, p), p) //Y3= M(S-X3)-W*Y1
                                            continue
                                        }
                                    }
                                    let T4 := mulmod(T2, T2, p)
                                    let T1 := mulmod(T4, T2, p) //
                                    zz := mulmod(zz, T4, p)
                                    //zzz3=V*ZZ1
                                    zzz := mulmod(zzz, T1, p) // W=UV/
                                    let zz1 := mulmod(X, T4, p)
                                    X := addmod(addmod(mulmod(y2, y2, p), sub(p, T1), p), mulmod(minus_2, zz1, p), p)
                                    Y := addmod(mulmod(addmod(zz1, sub(p, X), p), y2, p), mulmod(Y, T1, p), p)
                                }
                            } //end loop
                            mstore(add(T, 0x60), zz)
                            //(X,Y)=ecZZ_SetAff(X,Y,zz, zzz);
                            //T[0] = inverseModp_Hard(T[0], p); //1/zzz, inline modular inversion using precompile:
                            // Define length of base, exponent and modulus. 0x20 == 32 bytes
                            mstore(T, 0x20)
                            mstore(add(T, 0x20), 0x20)
                            mstore(add(T, 0x40), 0x20)
                            // Define variables base, exponent and modulus
                            //mstore(add(pointer, 0x60), u)
                            mstore(add(T, 0x80), minus_2)
                            mstore(add(T, 0xa0), p)
                            // Call the precompiled contract 0x05 = ModExp
                            if iszero(staticcall(not(0), 0x05, T, 0xc0, T, 0x20)) { revert(0, 0) }
                            zz := mload(T)
                            X := mulmod(X, zz, p) //X/zz
                        }
                    } //end unchecked
                }
               
                // improving the extcodecopy trick : append array at end of contract
                function ecZZ_mulmuladd_S8_hackmem(uint256 scalar_u, uint256 scalar_v, uint256 dataPointer)
                    internal view
                    returns (uint256 X /*, uint Y*/ )
                {
                    uint256 zz; // third and  coordinates of the point
                    uint256[6] memory T;
                    zz = 256; //start index
                    unchecked {
                        while (T[0] == 0) {
                            zz = zz - 1;
                            //tbd case of msb octobit is null
                            T[0] = 64
                                * (
                                    128 * ((scalar_v >> zz) & 1) + 64 * ((scalar_v >> (zz - 64)) & 1)
                                        + 32 * ((scalar_v >> (zz - 128)) & 1) + 16 * ((scalar_v >> (zz - 192)) & 1)
                                        + 8 * ((scalar_u >> zz) & 1) + 4 * ((scalar_u >> (zz - 64)) & 1)
                                        + 2 * ((scalar_u >> (zz - 128)) & 1) + ((scalar_u >> (zz - 192)) & 1)
                                );
                        }
                        assembly {
                            codecopy(T, add(mload(T), dataPointer), 64)
                            X := mload(T)
                            let Y := mload(add(T, 32))
                            let zzz := 1
                            zz := 1
                            //loop over 1/4 of scalars thx to Shamir's trick over 8 points
                            for { let index := 254 } gt(index, 191) { index := add(index, 191) } {
                                let T1 := mulmod(2, Y, p) //U = 2*Y1, y free
                                let T2 := mulmod(T1, T1, p) // V=U^2
                                let T3 := mulmod(X, T2, p) // S = X1*V
                                T1 := mulmod(T1, T2, p) // W=UV
                                let T4 := mulmod(3, mulmod(addmod(X, sub(p, zz), p), addmod(X, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1)
                                zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1
                                zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free
                                X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S
                                //T2:=mulmod(T4,addmod(T3, sub(p, X),p),p)//M(S-X3)
                                T2 := mulmod(T4, addmod(X, sub(p, T3), p), p) //-M(S-X3)=M(X3-S)
                                //Y:= addmod(T2, sub(p, mulmod(T1, Y ,p)),p  )//Y3= M(S-X3)-W*Y1
                                Y := addmod(mulmod(T1, Y, p), T2, p) //-Y3= W*Y1-M(S-X3), we replace Y by -Y to avoid a sub in ecAdd
                                /* compute element to access in precomputed table */
                                T4 := add(shl(13, and(shr(index, scalar_v), 1)), shl(9, and(shr(index, scalar_u), 1)))
                                index := sub(index, 64)
                                T4 := add(T4, add(shl(12, and(shr(index, scalar_v), 1)), shl(8, and(shr(index, scalar_u), 1))))
                                index := sub(index, 64)
                                T4 := add(T4, add(shl(11, and(shr(index, scalar_v), 1)), shl(7, and(shr(index, scalar_u), 1))))
                                index := sub(index, 64)
                                T4 := add(T4, add(shl(10, and(shr(index, scalar_v), 1)), shl(6, and(shr(index, scalar_u), 1))))
                                //index:=add(index,192), restore index, interleaved with loop
                                //tbd: check validity of formulae with (0,1) to remove conditional jump
                                if iszero(T4) {
                                    Y := sub(p, Y)
                                    continue
                                }
                                {
                                    /* Access to precomputed table using extcodecopy hack */
                                    codecopy(T, add(T4, dataPointer), 64)
                                    // inlined EcZZ_AddN
                                    let y2 := addmod(mulmod(mload(add(T, 32)), zzz, p), Y, p)
                                    T2 := addmod(mulmod(mload(T), zz, p), sub(p, X), p)
                                    T4 := mulmod(T2, T2, p)
                                    T1 := mulmod(T4, T2, p)
                                    T2 := mulmod(zz, T4, p) // W=UV
                                    zzz := mulmod(zzz, T1, p) //zz3=V*ZZ1
                                    let zz1 := mulmod(X, T4, p)
                                    T4 := addmod(addmod(mulmod(y2, y2, p), sub(p, T1), p), mulmod(minus_2, zz1, p), p)
                                    Y := addmod(mulmod(addmod(zz1, sub(p, T4), p), y2, p), mulmod(Y, T1, p), p)
                                    zz := T2
                                    X := T4
                                }
                            } //end loop
                            mstore(add(T, 0x60), zz)
                            //(X,Y)=ecZZ_SetAff(X,Y,zz, zzz);
                            //T[0] = inverseModp_Hard(T[0], p); //1/zzz, inline modular inversion using precompile:
                            // Define length of base, exponent and modulus. 0x20 == 32 bytes
                            mstore(T, 0x20)
                            mstore(add(T, 0x20), 0x20)
                            mstore(add(T, 0x40), 0x20)
                            // Define variables base, exponent and modulus
                            //mstore(add(pointer, 0x60), u)
                            mstore(add(T, 0x80), minus_2)
                            mstore(add(T, 0xa0), p)
                            // Call the precompiled contract 0x05 = ModExp
                            if iszero(staticcall(not(0), 0x05, T, 0xc0, T, 0x20)) { revert(0, 0) }
                            zz := mload(T)
                            X := mulmod(X, zz, p) //X/zz
                        }
                    } //end unchecked
                }
                /**
                 * @dev ECDSA verification using a precomputed table of multiples of P and Q stored in contract at address Shamir8
                 *     generation of contract bytecode for precomputations is done using sagemath code
                 *     (see sage directory, WebAuthn_precompute.sage)
                 */
                /**
                 * @dev ECDSA verification using a precomputed table of multiples of P and Q appended at end of contract at address endcontract
                 *     generation of contract bytecode for precomputations is done using sagemath code
                 *     (see sage directory, WebAuthn_precompute.sage)
                 */
                function ecdsa_precomputed_hackmem(bytes32 message, uint256[2] calldata rs, uint256 endcontract)
                    internal view
                    returns (bool)
                {
                    uint256 r = rs[0];
                    uint256 s = rs[1];
                    if (r == 0 || r >= n || s == 0 || s >= n) {
                        return false;
                    }
                    /* Q is pushed via bytecode assumed to be correct
                    if (!isOnCurve(Q[0], Q[1])) {
                        return false;
                    }*/
                    uint256 sInv = FCL_nModInv(s);
                    uint256 X;
                    //Shamir 8 dimensions
                    X = ecZZ_mulmuladd_S8_hackmem(mulmod(uint256(message), sInv, n), mulmod(r, sInv, n), endcontract);
                    assembly {
                        X := addmod(X, sub(n, r), n)
                    }
                    return X == 0;
                } //end  ecdsa_precomputed_verify()
            } //EOF
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v5.0.2) (utils/Base64.sol)
            pragma solidity ^0.8.20;
            /**
             * @dev Provides a set of functions to operate with Base64 strings.
             */
            library Base64 {
                /**
                 * @dev Base64 Encoding/Decoding Table
                 * See sections 4 and 5 of https://datatracker.ietf.org/doc/html/rfc4648
                 */
                string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
                string internal constant _TABLE_URL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
                /**
                 * @dev Converts a `bytes` to its Bytes64 `string` representation.
                 */
                function encode(bytes memory data) internal pure returns (string memory) {
                    return _encode(data, _TABLE, true);
                }
                /**
                 * @dev Converts a `bytes` to its Bytes64Url `string` representation.
                 */
                function encodeURL(bytes memory data) internal pure returns (string memory) {
                    return _encode(data, _TABLE_URL, false);
                }
                /**
                 * @dev Internal table-agnostic conversion
                 */
                function _encode(bytes memory data, string memory table, bool withPadding) private pure returns (string memory) {
                    /**
                     * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
                     * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
                     */
                    if (data.length == 0) return "";
                    // If padding is enabled, the final length should be `bytes` data length divided by 3 rounded up and then
                    // multiplied by 4 so that it leaves room for padding the last chunk
                    // - `data.length + 2`  -> Round up
                    // - `/ 3`              -> Number of 3-bytes chunks
                    // - `4 *`              -> 4 characters for each chunk
                    // If padding is disabled, the final length should be `bytes` data length multiplied by 4/3 rounded up as
                    // opposed to when padding is required to fill the last chunk.
                    // - `4 *`              -> 4 characters for each chunk
                    // - `data.length + 2`  -> Round up
                    // - `/ 3`              -> Number of 3-bytes chunks
                    uint256 resultLength = withPadding ? 4 * ((data.length + 2) / 3) : (4 * data.length + 2) / 3;
                    string memory result = new string(resultLength);
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Prepare the lookup table (skip the first "length" byte)
                        let tablePtr := add(table, 1)
                        // Prepare result pointer, jump over length
                        let resultPtr := add(result, 0x20)
                        let dataPtr := data
                        let endPtr := add(data, mload(data))
                        // In some cases, the last iteration will read bytes after the end of the data. We cache the value, and
                        // set it to zero to make sure no dirty bytes are read in that section.
                        let afterPtr := add(endPtr, 0x20)
                        let afterCache := mload(afterPtr)
                        mstore(afterPtr, 0x00)
                        // Run over the input, 3 bytes at a time
                        for {
                        } lt(dataPtr, endPtr) {
                        } {
                            // Advance 3 bytes
                            dataPtr := add(dataPtr, 3)
                            let input := mload(dataPtr)
                            // To write each character, shift the 3 byte (24 bits) chunk
                            // 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
                            // and apply logical AND with 0x3F to bitmask the least significant 6 bits.
                            // Use this as an index into the lookup table, mload an entire word
                            // so the desired character is in the least significant byte, and
                            // mstore8 this least significant byte into the result and continue.
                            mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
                            resultPtr := add(resultPtr, 1) // Advance
                            mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
                            resultPtr := add(resultPtr, 1) // Advance
                            mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
                            resultPtr := add(resultPtr, 1) // Advance
                            mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
                            resultPtr := add(resultPtr, 1) // Advance
                        }
                        // Reset the value that was cached
                        mstore(afterPtr, afterCache)
                        if withPadding {
                            // When data `bytes` is not exactly 3 bytes long
                            // it is padded with `=` characters at the end
                            switch mod(mload(data), 3)
                            case 1 {
                                mstore8(sub(resultPtr, 1), 0x3d)
                                mstore8(sub(resultPtr, 2), 0x3d)
                            }
                            case 2 {
                                mstore8(sub(resultPtr, 1), 0x3d)
                            }
                        }
                    }
                    return result;
                }
            }
            // 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)
                    }
                }
            }
            

            File 6 of 6: CoinbaseSmartWallet
            // SPDX-License-Identifier: MIT
            pragma solidity 0.8.23;
            import {IAccount} from "account-abstraction/interfaces/IAccount.sol";
            import {UserOperation, UserOperationLib} from "account-abstraction/interfaces/UserOperation.sol";
            import {Receiver} from "solady/accounts/Receiver.sol";
            import {SignatureCheckerLib} from "solady/utils/SignatureCheckerLib.sol";
            import {UUPSUpgradeable} from "solady/utils/UUPSUpgradeable.sol";
            import {WebAuthn} from "webauthn-sol/WebAuthn.sol";
            import {ERC1271} from "./ERC1271.sol";
            import {MultiOwnable} from "./MultiOwnable.sol";
            /// @title Coinbase Smart Wallet
            ///
            /// @notice ERC-4337-compatible smart account, based on Solady's ERC4337 account implementation
            ///         with inspiration from Alchemy's LightAccount and Daimo's DaimoAccount. Verified by z0r0z.eth from (⌘) NANI.eth
            ///
            /// @author Coinbase (https://github.com/coinbase/smart-wallet)
            /// @author Solady (https://github.com/vectorized/solady/blob/main/src/accounts/ERC4337.sol)
            contract CoinbaseSmartWallet is ERC1271, IAccount, MultiOwnable, UUPSUpgradeable, Receiver {
                /// @notice A wrapper struct used for signature validation so that callers
                ///         can identify the owner that signed.
                struct SignatureWrapper {
                    /// @dev The index of the owner that signed, see `MultiOwnable.ownerAtIndex`
                    uint256 ownerIndex;
                    /// @dev If `MultiOwnable.ownerAtIndex` is an Ethereum address, this should be `abi.encodePacked(r, s, v)`
                    ///      If `MultiOwnable.ownerAtIndex` is a public key, this should be `abi.encode(WebAuthnAuth)`.
                    bytes signatureData;
                }
                /// @notice Represents a call to make.
                struct Call {
                    /// @dev The address to call.
                    address target;
                    /// @dev The value to send when making the call.
                    uint256 value;
                    /// @dev The data of the call.
                    bytes data;
                }
                /// @notice Reserved nonce key (upper 192 bits of `UserOperation.nonce`) for cross-chain replayable
                ///         transactions.
                ///
                /// @dev MUST BE the `UserOperation.nonce` key when `UserOperation.calldata` is calling
                ///      `executeWithoutChainIdValidation`and MUST NOT BE `UserOperation.nonce` key when `UserOperation.calldata` is
                ///      NOT calling `executeWithoutChainIdValidation`.
                ///
                /// @dev Helps enforce sequential sequencing of replayable transactions.
                uint256 public constant REPLAYABLE_NONCE_KEY = 8453;
                /// @notice Thrown when `initialize` is called but the account already has had at least one owner.
                error Initialized();
                /// @notice Thrown when a call is passed to `executeWithoutChainIdValidation` that is not allowed by
                ///         `canSkipChainIdValidation`
                ///
                /// @param selector The selector of the call.
                error SelectorNotAllowed(bytes4 selector);
                /// @notice Thrown in validateUserOp if the key of `UserOperation.nonce` does not match the calldata.
                ///
                /// @dev Calls to `this.executeWithoutChainIdValidation` MUST use `REPLAYABLE_NONCE_KEY` and
                ///      calls NOT to `this.executeWithoutChainIdValidation` MUST NOT use `REPLAYABLE_NONCE_KEY`.
                ///
                /// @param key The invalid `UserOperation.nonce` key.
                error InvalidNonceKey(uint256 key);
                /// @notice Reverts if the caller is not the EntryPoint.
                modifier onlyEntryPoint() virtual {
                    if (msg.sender != entryPoint()) {
                        revert Unauthorized();
                    }
                    _;
                }
                /// @notice Reverts if the caller is neither the EntryPoint, the owner, nor the account itself.
                modifier onlyEntryPointOrOwner() virtual {
                    if (msg.sender != entryPoint()) {
                        _checkOwner();
                    }
                    _;
                }
                /// @notice Sends to the EntryPoint (i.e. `msg.sender`) the missing funds for this transaction.
                ///
                /// @dev Subclass MAY override this modifier for better funds management (e.g. send to the
                ///      EntryPoint more than the minimum required, so that in future transactions it will not
                ///      be required to send again).
                ///
                /// @param missingAccountFunds The minimum value this modifier should send the EntryPoint which
                ///                            MAY be zero, in case there is enough deposit, or the userOp has a
                ///                            paymaster.
                modifier payPrefund(uint256 missingAccountFunds) virtual {
                    _;
                    assembly ("memory-safe") {
                        if missingAccountFunds {
                            // Ignore failure (it's EntryPoint's job to verify, not the account's).
                            pop(call(gas(), caller(), missingAccountFunds, codesize(), 0x00, codesize(), 0x00))
                        }
                    }
                }
                constructor() {
                    // Implementation should not be initializable (does not affect proxies which use their own storage).
                    bytes[] memory owners = new bytes[](1);
                    owners[0] = abi.encode(address(0));
                    _initializeOwners(owners);
                }
                /// @notice Initializes the account with the `owners`.
                ///
                /// @dev Reverts if the account has had at least one owner, i.e. has been initialized.
                ///
                /// @param owners Array of initial owners for this account. Each item should be
                ///               an ABI encoded Ethereum address, i.e. 32 bytes with 12 leading 0 bytes,
                ///               or a 64 byte public key.
                function initialize(bytes[] calldata owners) external payable virtual {
                    if (nextOwnerIndex() != 0) {
                        revert Initialized();
                    }
                    _initializeOwners(owners);
                }
                /// @inheritdoc IAccount
                ///
                /// @notice ERC-4337 `validateUserOp` method. The EntryPoint will
                ///         call `UserOperation.sender.call(UserOperation.callData)` only if this validation call returns
                ///         successfully.
                ///
                /// @dev Signature failure should be reported by returning 1 (see: `this._isValidSignature`). This
                ///      allows making a "simulation call" without a valid signature. Other failures (e.g. invalid signature format)
                ///      should still revert to signal failure.
                /// @dev Reverts if the `UserOperation.nonce` key is invalid for `UserOperation.calldata`.
                /// @dev Reverts if the signature format is incorrect or invalid for owner type.
                ///
                /// @param userOp              The `UserOperation` to validate.
                /// @param userOpHash          The `UserOperation` hash, as computed by `EntryPoint.getUserOpHash(UserOperation)`.
                /// @param missingAccountFunds The missing account funds that must be deposited on the Entrypoint.
                ///
                /// @return validationData The encoded `ValidationData` structure:
                ///                        `(uint256(validAfter) << (160 + 48)) | (uint256(validUntil) << 160) | (success ? 0 : 1)`
                ///                        where `validUntil` is 0 (indefinite) and `validAfter` is 0.
                function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds)
                    external
                    virtual
                    onlyEntryPoint
                    payPrefund(missingAccountFunds)
                    returns (uint256 validationData)
                {
                    uint256 key = userOp.nonce >> 64;
                    if (bytes4(userOp.callData) == this.executeWithoutChainIdValidation.selector) {
                        userOpHash = getUserOpHashWithoutChainId(userOp);
                        if (key != REPLAYABLE_NONCE_KEY) {
                            revert InvalidNonceKey(key);
                        }
                    } else {
                        if (key == REPLAYABLE_NONCE_KEY) {
                            revert InvalidNonceKey(key);
                        }
                    }
                    // Return 0 if the recovered address matches the owner.
                    if (_isValidSignature(userOpHash, userOp.signature)) {
                        return 0;
                    }
                    // Else return 1
                    return 1;
                }
                /// @notice Executes `calls` on this account (i.e. self call).
                ///
                /// @dev Can only be called by the Entrypoint.
                /// @dev Reverts if the given call is not authorized to skip the chain ID validtion.
                /// @dev `validateUserOp()` will recompute the `userOpHash` without the chain ID before validating
                ///      it if the `UserOperation.calldata` is calling this function. This allows certain UserOperations
                ///      to be replayed for all accounts sharing the same address across chains. E.g. This may be
                ///      useful for syncing owner changes.
                ///
                /// @param calls An array of calldata to use for separate self calls.
                function executeWithoutChainIdValidation(bytes[] calldata calls) external payable virtual onlyEntryPoint {
                    for (uint256 i; i < calls.length; i++) {
                        bytes calldata call = calls[i];
                        bytes4 selector = bytes4(call);
                        if (!canSkipChainIdValidation(selector)) {
                            revert SelectorNotAllowed(selector);
                        }
                        _call(address(this), 0, call);
                    }
                }
                /// @notice Executes the given call from this account.
                ///
                /// @dev Can only be called by the Entrypoint or an owner of this account (including itself).
                ///
                /// @param target The address to call.
                /// @param value  The value to send with the call.
                /// @param data   The data of the call.
                function execute(address target, uint256 value, bytes calldata data)
                    external
                    payable
                    virtual
                    onlyEntryPointOrOwner
                {
                    _call(target, value, data);
                }
                /// @notice Executes batch of `Call`s.
                ///
                /// @dev Can only be called by the Entrypoint or an owner of this account (including itself).
                ///
                /// @param calls The list of `Call`s to execute.
                function executeBatch(Call[] calldata calls) external payable virtual onlyEntryPointOrOwner {
                    for (uint256 i; i < calls.length; i++) {
                        _call(calls[i].target, calls[i].value, calls[i].data);
                    }
                }
                /// @notice Returns the address of the EntryPoint v0.6.
                ///
                /// @return The address of the EntryPoint v0.6
                function entryPoint() public view virtual returns (address) {
                    return 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789;
                }
                /// @notice Computes the hash of the `UserOperation` in the same way as EntryPoint v0.6, but
                ///         leaves out the chain ID.
                ///
                /// @dev This allows accounts to sign a hash that can be used on many chains.
                ///
                /// @param userOp The `UserOperation` to compute the hash for.
                ///
                /// @return The `UserOperation` hash, which does not depend on chain ID.
                function getUserOpHashWithoutChainId(UserOperation calldata userOp) public view virtual returns (bytes32) {
                    return keccak256(abi.encode(UserOperationLib.hash(userOp), entryPoint()));
                }
                /// @notice Returns the implementation of the ERC1967 proxy.
                ///
                /// @return $ The address of implementation contract.
                function implementation() public view returns (address $) {
                    assembly {
                        $ := sload(_ERC1967_IMPLEMENTATION_SLOT)
                    }
                }
                /// @notice Returns whether `functionSelector` can be called in `executeWithoutChainIdValidation`.
                ///
                /// @param functionSelector The function selector to check.
                ////
                /// @return `true` is the function selector is allowed to skip the chain ID validation, else `false`.
                function canSkipChainIdValidation(bytes4 functionSelector) public pure returns (bool) {
                    if (
                        functionSelector == MultiOwnable.addOwnerPublicKey.selector
                            || functionSelector == MultiOwnable.addOwnerAddress.selector
                            || functionSelector == MultiOwnable.removeOwnerAtIndex.selector
                            || functionSelector == MultiOwnable.removeLastOwner.selector
                            || functionSelector == UUPSUpgradeable.upgradeToAndCall.selector
                    ) {
                        return true;
                    }
                    return false;
                }
                /// @notice Executes the given call from this account.
                ///
                /// @dev Reverts if the call reverted.
                /// @dev Implementation taken from
                /// https://github.com/alchemyplatform/light-account/blob/43f625afdda544d5e5af9c370c9f4be0943e4e90/src/common/BaseLightAccount.sol#L125
                ///
                /// @param target The target call address.
                /// @param value  The call value to user.
                /// @param data   The raw call data.
                function _call(address target, uint256 value, bytes memory data) internal {
                    (bool success, bytes memory result) = target.call{value: value}(data);
                    if (!success) {
                        assembly ("memory-safe") {
                            revert(add(result, 32), mload(result))
                        }
                    }
                }
                /// @inheritdoc ERC1271
                ///
                /// @dev Used by both `ERC1271.isValidSignature` AND `IAccount.validateUserOp` signature validation.
                /// @dev Reverts if owner at `ownerIndex` is not compatible with `signature` format.
                ///
                /// @param signature ABI encoded `SignatureWrapper`.
                function _isValidSignature(bytes32 hash, bytes calldata signature) internal view virtual override returns (bool) {
                    SignatureWrapper memory sigWrapper = abi.decode(signature, (SignatureWrapper));
                    bytes memory ownerBytes = ownerAtIndex(sigWrapper.ownerIndex);
                    if (ownerBytes.length == 32) {
                        if (uint256(bytes32(ownerBytes)) > type(uint160).max) {
                            // technically should be impossible given owners can only be added with
                            // addOwnerAddress and addOwnerPublicKey, but we leave incase of future changes.
                            revert InvalidEthereumAddressOwner(ownerBytes);
                        }
                        address owner;
                        assembly ("memory-safe") {
                            owner := mload(add(ownerBytes, 32))
                        }
                        return SignatureCheckerLib.isValidSignatureNow(owner, hash, sigWrapper.signatureData);
                    }
                    if (ownerBytes.length == 64) {
                        (uint256 x, uint256 y) = abi.decode(ownerBytes, (uint256, uint256));
                        WebAuthn.WebAuthnAuth memory auth = abi.decode(sigWrapper.signatureData, (WebAuthn.WebAuthnAuth));
                        return WebAuthn.verify({challenge: abi.encode(hash), requireUV: false, webAuthnAuth: auth, x: x, y: y});
                    }
                    revert InvalidOwnerBytesLength(ownerBytes);
                }
                /// @inheritdoc UUPSUpgradeable
                ///
                /// @dev Authorization logic is only based on the `msg.sender` being an owner of this account,
                ///      or `address(this)`.
                function _authorizeUpgrade(address) internal view virtual override(UUPSUpgradeable) onlyOwner {}
                /// @inheritdoc ERC1271
                function _domainNameAndVersion() internal pure override(ERC1271) returns (string memory, string memory) {
                    return ("Coinbase Smart Wallet", "1");
                }
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.8.12;
            import "./UserOperation.sol";
            interface IAccount {
                /**
                 * Validate user's signature and nonce
                 * the entryPoint will make the call to the recipient only if this validation call returns successfully.
                 * signature failure should be reported by returning SIG_VALIDATION_FAILED (1).
                 * This allows making a "simulation call" without a valid signature
                 * Other failures (e.g. nonce mismatch, or invalid signature format) should still revert to signal failure.
                 *
                 * @dev Must validate caller is the entryPoint.
                 *      Must validate the signature and nonce
                 * @param userOp the operation that is about to be executed.
                 * @param userOpHash hash of the user's request data. can be used as the basis for signature.
                 * @param missingAccountFunds missing funds on the account's deposit in the entrypoint.
                 *      This is the minimum amount to transfer to the sender(entryPoint) to be able to make the call.
                 *      The excess is left as a deposit in the entrypoint, for future calls.
                 *      can be withdrawn anytime using "entryPoint.withdrawTo()"
                 *      In case there is a paymaster in the request (or the current deposit is high enough), this value will be zero.
                 * @return validationData packaged ValidationData structure. use `_packValidationData` and `_unpackValidationData` to encode and decode
                 *      <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure,
                 *         otherwise, an address of an "authorizer" contract.
                 *      <6-byte> validUntil - last timestamp this operation is valid. 0 for "indefinite"
                 *      <6-byte> validAfter - first timestamp this operation is valid
                 *      If an account doesn't use time-range, it is enough to return SIG_VALIDATION_FAILED value (1) for signature failure.
                 *      Note that the validation code cannot use block.timestamp (or block.number) directly.
                 */
                function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds)
                external returns (uint256 validationData);
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.8.12;
            /* solhint-disable no-inline-assembly */
            import {calldataKeccak} from "../core/Helpers.sol";
            /**
             * User Operation struct
             * @param sender the sender account of this request.
                 * @param nonce unique value the sender uses to verify it is not a replay.
                 * @param initCode if set, the account contract will be created by this constructor/
                 * @param callData the method call to execute on this account.
                 * @param callGasLimit the gas limit passed to the callData method call.
                 * @param verificationGasLimit gas used for validateUserOp and validatePaymasterUserOp.
                 * @param preVerificationGas gas not calculated by the handleOps method, but added to the gas paid. Covers batch overhead.
                 * @param maxFeePerGas same as EIP-1559 gas parameter.
                 * @param maxPriorityFeePerGas same as EIP-1559 gas parameter.
                 * @param paymasterAndData if set, this field holds the paymaster address and paymaster-specific data. the paymaster will pay for the transaction instead of the sender.
                 * @param signature sender-verified signature over the entire request, the EntryPoint address and the chain ID.
                 */
                struct UserOperation {
                    address sender;
                    uint256 nonce;
                    bytes initCode;
                    bytes callData;
                    uint256 callGasLimit;
                    uint256 verificationGasLimit;
                    uint256 preVerificationGas;
                    uint256 maxFeePerGas;
                    uint256 maxPriorityFeePerGas;
                    bytes paymasterAndData;
                    bytes signature;
                }
            /**
             * Utility functions helpful when working with UserOperation structs.
             */
            library UserOperationLib {
                function getSender(UserOperation calldata userOp) internal pure returns (address) {
                    address data;
                    //read sender from userOp, which is first userOp member (saves 800 gas...)
                    assembly {data := calldataload(userOp)}
                    return address(uint160(data));
                }
                //relayer/block builder might submit the TX with higher priorityFee, but the user should not
                // pay above what he signed for.
                function gasPrice(UserOperation calldata userOp) internal view returns (uint256) {
                unchecked {
                    uint256 maxFeePerGas = userOp.maxFeePerGas;
                    uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas;
                    if (maxFeePerGas == maxPriorityFeePerGas) {
                        //legacy mode (for networks that don't support basefee opcode)
                        return maxFeePerGas;
                    }
                    return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee);
                }
                }
                function pack(UserOperation calldata userOp) internal pure returns (bytes memory ret) {
                    address sender = getSender(userOp);
                    uint256 nonce = userOp.nonce;
                    bytes32 hashInitCode = calldataKeccak(userOp.initCode);
                    bytes32 hashCallData = calldataKeccak(userOp.callData);
                    uint256 callGasLimit = userOp.callGasLimit;
                    uint256 verificationGasLimit = userOp.verificationGasLimit;
                    uint256 preVerificationGas = userOp.preVerificationGas;
                    uint256 maxFeePerGas = userOp.maxFeePerGas;
                    uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas;
                    bytes32 hashPaymasterAndData = calldataKeccak(userOp.paymasterAndData);
                    return abi.encode(
                        sender, nonce,
                        hashInitCode, hashCallData,
                        callGasLimit, verificationGasLimit, preVerificationGas,
                        maxFeePerGas, maxPriorityFeePerGas,
                        hashPaymasterAndData
                    );
                }
                function hash(UserOperation calldata userOp) internal pure returns (bytes32) {
                    return keccak256(pack(userOp));
                }
                function min(uint256 a, uint256 b) internal pure returns (uint256) {
                    return a < b ? a : b;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.4;
            /// @notice Receiver mixin for ETH and safe-transferred ERC721 and ERC1155 tokens.
            /// @author Solady (https://github.com/Vectorized/solady/blob/main/src/accounts/Receiver.sol)
            ///
            /// @dev Note:
            /// - Handles all ERC721 and ERC1155 token safety callbacks.
            /// - Collapses function table gas overhead and code size.
            /// - Utilizes fallback so unknown calldata will pass on.
            abstract contract Receiver {
                /// @dev For receiving ETH.
                receive() external payable virtual {}
                /// @dev Fallback function with the `receiverFallback` modifier.
                fallback() external payable virtual receiverFallback {}
                /// @dev Modifier for the fallback function to handle token callbacks.
                modifier receiverFallback() virtual {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let s := shr(224, calldataload(0))
                        // 0x150b7a02: `onERC721Received(address,address,uint256,bytes)`.
                        // 0xf23a6e61: `onERC1155Received(address,address,uint256,uint256,bytes)`.
                        // 0xbc197c81: `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`.
                        if or(eq(s, 0x150b7a02), or(eq(s, 0xf23a6e61), eq(s, 0xbc197c81))) {
                            mstore(0x20, s) // Store `msg.sig`.
                            return(0x3c, 0x20) // Return `msg.sig`.
                        }
                    }
                    _;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.4;
            /// @notice Signature verification helper that supports both ECDSA signatures from EOAs
            /// and ERC1271 signatures from smart contract wallets like Argent and Gnosis safe.
            /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SignatureCheckerLib.sol)
            /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/SignatureChecker.sol)
            ///
            /// @dev Note:
            /// - The signature checking functions use the ecrecover precompile (0x1).
            /// - The `bytes memory signature` variants use the identity precompile (0x4)
            ///   to copy memory internally.
            /// - Unlike ECDSA signatures, contract signatures are revocable.
            /// - 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 SignatureCheckerLib {
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*               SIGNATURE CHECKING OPERATIONS                */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Returns whether `signature` is valid for `signer` and `hash`.
                /// If `signer` is a smart contract, the signature is validated with ERC1271.
                /// Otherwise, the signature is validated with `ECDSA.recover`.
                function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature)
                    internal
                    view
                    returns (bool isValid)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Clean the upper 96 bits of `signer` in case they are dirty.
                        for { signer := shr(96, shl(96, signer)) } signer {} {
                            let m := mload(0x40)
                            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`.
                                let t :=
                                    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(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                                    isValid := 1
                                    mstore(0x60, 0) // Restore the zero slot.
                                    mstore(0x40, m) // Restore the free memory pointer.
                                    break
                                }
                            }
                            if eq(mload(signature), 65) {
                                mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                                mstore(0x60, mload(add(signature, 0x40))) // `s`.
                                let t :=
                                    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(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                                    isValid := 1
                                    mstore(0x60, 0) // Restore the zero slot.
                                    mstore(0x40, m) // Restore the free memory pointer.
                                    break
                                }
                            }
                            mstore(0x60, 0) // Restore the zero slot.
                            mstore(0x40, m) // Restore the free memory pointer.
                            let f := shl(224, 0x1626ba7e)
                            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                            mstore(add(m, 0x04), hash)
                            let d := add(m, 0x24)
                            mstore(d, 0x40) // The offset of the `signature` in the calldata.
                            // Copy the `signature` over.
                            let n := add(0x20, mload(signature))
                            pop(staticcall(gas(), 4, signature, n, add(m, 0x44), n))
                            // forgefmt: disable-next-item
                            isValid := and(
                                // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                                eq(mload(d), f),
                                // Whether the staticcall does not revert.
                                // This must be placed at the end of the `and` clause,
                                // as the arguments are evaluated from right to left.
                                staticcall(
                                    gas(), // Remaining gas.
                                    signer, // The `signer` address.
                                    m, // Offset of calldata in memory.
                                    add(returndatasize(), 0x44), // Length of calldata in memory.
                                    d, // Offset of returndata.
                                    0x20 // Length of returndata to write.
                                )
                            )
                            break
                        }
                    }
                }
                /// @dev Returns whether `signature` is valid for `signer` and `hash`.
                /// If `signer` is a smart contract, the signature is validated with ERC1271.
                /// Otherwise, the signature is validated with `ECDSA.recover`.
                function isValidSignatureNowCalldata(address signer, bytes32 hash, bytes calldata signature)
                    internal
                    view
                    returns (bool isValid)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Clean the upper 96 bits of `signer` in case they are dirty.
                        for { signer := shr(96, shl(96, signer)) } signer {} {
                            let m := mload(0x40)
                            mstore(0x00, hash)
                            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`.
                                let t :=
                                    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(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                                    isValid := 1
                                    mstore(0x60, 0) // Restore the zero slot.
                                    mstore(0x40, m) // Restore the free memory pointer.
                                    break
                                }
                            }
                            if eq(signature.length, 65) {
                                mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                                calldatacopy(0x40, signature.offset, 0x40) // `r`, `s`.
                                let t :=
                                    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(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                                    isValid := 1
                                    mstore(0x60, 0) // Restore the zero slot.
                                    mstore(0x40, m) // Restore the free memory pointer.
                                    break
                                }
                            }
                            mstore(0x60, 0) // Restore the zero slot.
                            mstore(0x40, m) // Restore the free memory pointer.
                            let f := shl(224, 0x1626ba7e)
                            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                            mstore(add(m, 0x04), hash)
                            let d := add(m, 0x24)
                            mstore(d, 0x40) // The offset of the `signature` in the calldata.
                            mstore(add(m, 0x44), signature.length)
                            // Copy the `signature` over.
                            calldatacopy(add(m, 0x64), signature.offset, signature.length)
                            // forgefmt: disable-next-item
                            isValid := and(
                                // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                                eq(mload(d), f),
                                // Whether the staticcall does not revert.
                                // This must be placed at the end of the `and` clause,
                                // as the arguments are evaluated from right to left.
                                staticcall(
                                    gas(), // Remaining gas.
                                    signer, // The `signer` address.
                                    m, // Offset of calldata in memory.
                                    add(signature.length, 0x64), // Length of calldata in memory.
                                    d, // Offset of returndata.
                                    0x20 // Length of returndata to write.
                                )
                            )
                            break
                        }
                    }
                }
                /// @dev Returns whether the signature (`r`, `vs`) is valid for `signer` and `hash`.
                /// If `signer` is a smart contract, the signature is validated with ERC1271.
                /// Otherwise, the signature is validated with `ECDSA.recover`.
                function isValidSignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs)
                    internal
                    view
                    returns (bool isValid)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Clean the upper 96 bits of `signer` in case they are dirty.
                        for { signer := shr(96, shl(96, signer)) } signer {} {
                            let m := mload(0x40)
                            mstore(0x00, hash)
                            mstore(0x20, add(shr(255, vs), 27)) // `v`.
                            mstore(0x40, r) // `r`.
                            mstore(0x60, shr(1, shl(1, vs))) // `s`.
                            let t :=
                                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(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                                isValid := 1
                                mstore(0x60, 0) // Restore the zero slot.
                                mstore(0x40, m) // Restore the free memory pointer.
                                break
                            }
                            let f := shl(224, 0x1626ba7e)
                            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                            mstore(add(m, 0x04), hash)
                            let d := add(m, 0x24)
                            mstore(d, 0x40) // The offset of the `signature` in the calldata.
                            mstore(add(m, 0x44), 65) // Length of the signature.
                            mstore(add(m, 0x64), r) // `r`.
                            mstore(add(m, 0x84), mload(0x60)) // `s`.
                            mstore8(add(m, 0xa4), mload(0x20)) // `v`.
                            // forgefmt: disable-next-item
                            isValid := and(
                                // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                                eq(mload(d), f),
                                // Whether the staticcall does not revert.
                                // This must be placed at the end of the `and` clause,
                                // as the arguments are evaluated from right to left.
                                staticcall(
                                    gas(), // Remaining gas.
                                    signer, // The `signer` address.
                                    m, // Offset of calldata in memory.
                                    0xa5, // Length of calldata in memory.
                                    d, // Offset of returndata.
                                    0x20 // Length of returndata to write.
                                )
                            )
                            mstore(0x60, 0) // Restore the zero slot.
                            mstore(0x40, m) // Restore the free memory pointer.
                            break
                        }
                    }
                }
                /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `signer` and `hash`.
                /// If `signer` is a smart contract, the signature is validated with ERC1271.
                /// Otherwise, the signature is validated with `ECDSA.recover`.
                function isValidSignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s)
                    internal
                    view
                    returns (bool isValid)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Clean the upper 96 bits of `signer` in case they are dirty.
                        for { signer := shr(96, shl(96, signer)) } signer {} {
                            let m := mload(0x40)
                            mstore(0x00, hash)
                            mstore(0x20, and(v, 0xff)) // `v`.
                            mstore(0x40, r) // `r`.
                            mstore(0x60, s) // `s`.
                            let t :=
                                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(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                                isValid := 1
                                mstore(0x60, 0) // Restore the zero slot.
                                mstore(0x40, m) // Restore the free memory pointer.
                                break
                            }
                            let f := shl(224, 0x1626ba7e)
                            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                            mstore(add(m, 0x04), hash)
                            let d := add(m, 0x24)
                            mstore(d, 0x40) // The offset of the `signature` in the calldata.
                            mstore(add(m, 0x44), 65) // Length of the signature.
                            mstore(add(m, 0x64), r) // `r`.
                            mstore(add(m, 0x84), s) // `s`.
                            mstore8(add(m, 0xa4), v) // `v`.
                            // forgefmt: disable-next-item
                            isValid := and(
                                // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                                eq(mload(d), f),
                                // Whether the staticcall does not revert.
                                // This must be placed at the end of the `and` clause,
                                // as the arguments are evaluated from right to left.
                                staticcall(
                                    gas(), // Remaining gas.
                                    signer, // The `signer` address.
                                    m, // Offset of calldata in memory.
                                    0xa5, // Length of calldata in memory.
                                    d, // Offset of returndata.
                                    0x20 // Length of returndata to write.
                                )
                            )
                            mstore(0x60, 0) // Restore the zero slot.
                            mstore(0x40, m) // Restore the free memory pointer.
                            break
                        }
                    }
                }
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                     ERC1271 OPERATIONS                     */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract.
                function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes memory signature)
                    internal
                    view
                    returns (bool isValid)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let m := mload(0x40)
                        let f := shl(224, 0x1626ba7e)
                        mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                        mstore(add(m, 0x04), hash)
                        let d := add(m, 0x24)
                        mstore(d, 0x40) // The offset of the `signature` in the calldata.
                        // Copy the `signature` over.
                        let n := add(0x20, mload(signature))
                        pop(staticcall(gas(), 4, signature, n, add(m, 0x44), n))
                        // forgefmt: disable-next-item
                        isValid := and(
                            // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                            eq(mload(d), f),
                            // Whether the staticcall does not revert.
                            // This must be placed at the end of the `and` clause,
                            // as the arguments are evaluated from right to left.
                            staticcall(
                                gas(), // Remaining gas.
                                signer, // The `signer` address.
                                m, // Offset of calldata in memory.
                                add(returndatasize(), 0x44), // Length of calldata in memory.
                                d, // Offset of returndata.
                                0x20 // Length of returndata to write.
                            )
                        )
                    }
                }
                /// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract.
                function isValidERC1271SignatureNowCalldata(
                    address signer,
                    bytes32 hash,
                    bytes calldata signature
                ) internal view returns (bool isValid) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let m := mload(0x40)
                        let f := shl(224, 0x1626ba7e)
                        mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                        mstore(add(m, 0x04), hash)
                        let d := add(m, 0x24)
                        mstore(d, 0x40) // The offset of the `signature` in the calldata.
                        mstore(add(m, 0x44), signature.length)
                        // Copy the `signature` over.
                        calldatacopy(add(m, 0x64), signature.offset, signature.length)
                        // forgefmt: disable-next-item
                        isValid := and(
                            // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                            eq(mload(d), f),
                            // Whether the staticcall does not revert.
                            // This must be placed at the end of the `and` clause,
                            // as the arguments are evaluated from right to left.
                            staticcall(
                                gas(), // Remaining gas.
                                signer, // The `signer` address.
                                m, // Offset of calldata in memory.
                                add(signature.length, 0x64), // Length of calldata in memory.
                                d, // Offset of returndata.
                                0x20 // Length of returndata to write.
                            )
                        )
                    }
                }
                /// @dev Returns whether the signature (`r`, `vs`) is valid for `hash`
                /// for an ERC1271 `signer` contract.
                function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs)
                    internal
                    view
                    returns (bool isValid)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let m := mload(0x40)
                        let f := shl(224, 0x1626ba7e)
                        mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                        mstore(add(m, 0x04), hash)
                        let d := add(m, 0x24)
                        mstore(d, 0x40) // The offset of the `signature` in the calldata.
                        mstore(add(m, 0x44), 65) // Length of the signature.
                        mstore(add(m, 0x64), r) // `r`.
                        mstore(add(m, 0x84), shr(1, shl(1, vs))) // `s`.
                        mstore8(add(m, 0xa4), add(shr(255, vs), 27)) // `v`.
                        // forgefmt: disable-next-item
                        isValid := and(
                            // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                            eq(mload(d), f),
                            // Whether the staticcall does not revert.
                            // This must be placed at the end of the `and` clause,
                            // as the arguments are evaluated from right to left.
                            staticcall(
                                gas(), // Remaining gas.
                                signer, // The `signer` address.
                                m, // Offset of calldata in memory.
                                0xa5, // Length of calldata in memory.
                                d, // Offset of returndata.
                                0x20 // Length of returndata to write.
                            )
                        )
                    }
                }
                /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `hash`
                /// for an ERC1271 `signer` contract.
                function isValidERC1271SignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s)
                    internal
                    view
                    returns (bool isValid)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let m := mload(0x40)
                        let f := shl(224, 0x1626ba7e)
                        mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                        mstore(add(m, 0x04), hash)
                        let d := add(m, 0x24)
                        mstore(d, 0x40) // The offset of the `signature` in the calldata.
                        mstore(add(m, 0x44), 65) // Length of the signature.
                        mstore(add(m, 0x64), r) // `r`.
                        mstore(add(m, 0x84), s) // `s`.
                        mstore8(add(m, 0xa4), v) // `v`.
                        // forgefmt: disable-next-item
                        isValid := and(
                            // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                            eq(mload(d), f),
                            // Whether the staticcall does not revert.
                            // This must be placed at the end of the `and` clause,
                            // as the arguments are evaluated from right to left.
                            staticcall(
                                gas(), // Remaining gas.
                                signer, // The `signer` address.
                                m, // Offset of calldata in memory.
                                0xa5, // Length of calldata in memory.
                                d, // Offset of returndata.
                                0x20 // Length of returndata to write.
                            )
                        )
                    }
                }
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                     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 UUPS proxy mixin.
            /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/UUPSUpgradeable.sol)
            /// @author Modified from OpenZeppelin
            /// (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/utils/UUPSUpgradeable.sol)
            ///
            /// Note:
            /// - This implementation is intended to be used with ERC1967 proxies.
            /// See: `LibClone.deployERC1967` and related functions.
            /// - This implementation is NOT compatible with legacy OpenZeppelin proxies
            /// which do not store the implementation at `_ERC1967_IMPLEMENTATION_SLOT`.
            abstract contract UUPSUpgradeable {
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                       CUSTOM ERRORS                        */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev The upgrade failed.
                error UpgradeFailed();
                /// @dev The call is from an unauthorized call context.
                error UnauthorizedCallContext();
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                         IMMUTABLES                         */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev For checking if the context is a delegate call.
                uint256 private immutable __self = uint256(uint160(address(this)));
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                           EVENTS                           */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Emitted when the proxy's implementation is upgraded.
                event Upgraded(address indexed implementation);
                /// @dev `keccak256(bytes("Upgraded(address)"))`.
                uint256 private constant _UPGRADED_EVENT_SIGNATURE =
                    0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b;
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                          STORAGE                           */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev The ERC-1967 storage slot for the implementation in the proxy.
                /// `uint256(keccak256("eip1967.proxy.implementation")) - 1`.
                bytes32 internal constant _ERC1967_IMPLEMENTATION_SLOT =
                    0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                      UUPS OPERATIONS                       */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Please override this function to check if `msg.sender` is authorized
                /// to upgrade the proxy to `newImplementation`, reverting if not.
                /// ```
                ///     function _authorizeUpgrade(address) internal override onlyOwner {}
                /// ```
                function _authorizeUpgrade(address newImplementation) internal virtual;
                /// @dev Returns the storage slot used by the implementation,
                /// as specified in [ERC1822](https://eips.ethereum.org/EIPS/eip-1822).
                ///
                /// Note: The `notDelegated` modifier prevents accidental upgrades to
                /// an implementation that is a proxy contract.
                function proxiableUUID() public view virtual notDelegated returns (bytes32) {
                    // This function must always return `_ERC1967_IMPLEMENTATION_SLOT` to comply with ERC1967.
                    return _ERC1967_IMPLEMENTATION_SLOT;
                }
                /// @dev Upgrades the proxy's implementation to `newImplementation`.
                /// Emits a {Upgraded} event.
                ///
                /// Note: Passing in empty `data` skips the delegatecall to `newImplementation`.
                function upgradeToAndCall(address newImplementation, bytes calldata data)
                    public
                    payable
                    virtual
                    onlyProxy
                {
                    _authorizeUpgrade(newImplementation);
                    /// @solidity memory-safe-assembly
                    assembly {
                        newImplementation := shr(96, shl(96, newImplementation)) // Clears upper 96 bits.
                        mstore(0x01, 0x52d1902d) // `proxiableUUID()`.
                        let s := _ERC1967_IMPLEMENTATION_SLOT
                        // Check if `newImplementation` implements `proxiableUUID` correctly.
                        if iszero(eq(mload(staticcall(gas(), newImplementation, 0x1d, 0x04, 0x01, 0x20)), s)) {
                            mstore(0x01, 0x55299b49) // `UpgradeFailed()`.
                            revert(0x1d, 0x04)
                        }
                        // Emit the {Upgraded} event.
                        log2(codesize(), 0x00, _UPGRADED_EVENT_SIGNATURE, newImplementation)
                        sstore(s, newImplementation) // Updates the implementation.
                        // Perform a delegatecall to `newImplementation` if `data` is non-empty.
                        if data.length {
                            // Forwards the `data` to `newImplementation` via delegatecall.
                            let m := mload(0x40)
                            calldatacopy(m, data.offset, data.length)
                            if iszero(delegatecall(gas(), newImplementation, m, data.length, codesize(), 0x00))
                            {
                                // Bubble up the revert if the call reverts.
                                returndatacopy(m, 0x00, returndatasize())
                                revert(m, returndatasize())
                            }
                        }
                    }
                }
                /// @dev Requires that the execution is performed through a proxy.
                modifier onlyProxy() {
                    uint256 s = __self;
                    /// @solidity memory-safe-assembly
                    assembly {
                        // To enable use cases with an immutable default implementation in the bytecode,
                        // (see: ERC6551Proxy), we don't require that the proxy address must match the
                        // value stored in the implementation slot, which may not be initialized.
                        if eq(s, address()) {
                            mstore(0x00, 0x9f03a026) // `UnauthorizedCallContext()`.
                            revert(0x1c, 0x04)
                        }
                    }
                    _;
                }
                /// @dev Requires that the execution is NOT performed via delegatecall.
                /// This is the opposite of `onlyProxy`.
                modifier notDelegated() {
                    uint256 s = __self;
                    /// @solidity memory-safe-assembly
                    assembly {
                        if iszero(eq(s, address())) {
                            mstore(0x00, 0x9f03a026) // `UnauthorizedCallContext()`.
                            revert(0x1c, 0x04)
                        }
                    }
                    _;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.0;
            import {FCL_ecdsa} from "FreshCryptoLib/FCL_ecdsa.sol";
            import {FCL_Elliptic_ZZ} from "FreshCryptoLib/FCL_elliptic.sol";
            import {Base64} from "openzeppelin-contracts/contracts/utils/Base64.sol";
            import {LibString} from "solady/utils/LibString.sol";
            /// @title WebAuthn
            ///
            /// @notice A library for verifying WebAuthn Authentication Assertions, built off the work
            ///         of Daimo.
            ///
            /// @dev Attempts to use the RIP-7212 precompile for signature verification.
            ///      If precompile verification fails, it falls back to FreshCryptoLib.
            ///
            /// @author Coinbase (https://github.com/base-org/webauthn-sol)
            /// @author Daimo (https://github.com/daimo-eth/p256-verifier/blob/master/src/WebAuthn.sol)
            library WebAuthn {
                using LibString for string;
                struct WebAuthnAuth {
                    /// @dev The WebAuthn authenticator data.
                    ///      See https://www.w3.org/TR/webauthn-2/#dom-authenticatorassertionresponse-authenticatordata.
                    bytes authenticatorData;
                    /// @dev The WebAuthn client data JSON.
                    ///      See https://www.w3.org/TR/webauthn-2/#dom-authenticatorresponse-clientdatajson.
                    string clientDataJSON;
                    /// @dev The index at which "challenge":"..." occurs in `clientDataJSON`.
                    uint256 challengeIndex;
                    /// @dev The index at which "type":"..." occurs in `clientDataJSON`.
                    uint256 typeIndex;
                    /// @dev The r value of secp256r1 signature
                    uint256 r;
                    /// @dev The s value of secp256r1 signature
                    uint256 s;
                }
                /// @dev Bit 0 of the authenticator data struct, corresponding to the "User Present" bit.
                ///      See https://www.w3.org/TR/webauthn-2/#flags.
                bytes1 private constant _AUTH_DATA_FLAGS_UP = 0x01;
                /// @dev Bit 2 of the authenticator data struct, corresponding to the "User Verified" bit.
                ///      See https://www.w3.org/TR/webauthn-2/#flags.
                bytes1 private constant _AUTH_DATA_FLAGS_UV = 0x04;
                /// @dev Secp256r1 curve order / 2 used as guard to prevent signature malleability issue.
                uint256 private constant _P256_N_DIV_2 = FCL_Elliptic_ZZ.n / 2;
                /// @dev The precompiled contract address to use for signature verification in the “secp256r1” elliptic curve.
                ///      See https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md.
                address private constant _VERIFIER = address(0x100);
                /// @dev The expected type (hash) in the client data JSON when verifying assertion signatures.
                ///      See https://www.w3.org/TR/webauthn-2/#dom-collectedclientdata-type
                bytes32 private constant _EXPECTED_TYPE_HASH = keccak256('"type":"webauthn.get"');
                ///
                /// @notice Verifies a Webauthn Authentication Assertion as described
                /// in https://www.w3.org/TR/webauthn-2/#sctn-verifying-assertion.
                ///
                /// @dev We do not verify all the steps as described in the specification, only ones relevant to our context.
                ///      Please carefully read through this list before usage.
                ///
                ///      Specifically, we do verify the following:
                ///         - Verify that authenticatorData (which comes from the authenticator, such as iCloud Keychain) indicates
                ///           a well-formed assertion with the user present bit set. If `requireUV` is set, checks that the authenticator
                ///           enforced user verification. User verification should be required if, and only if, options.userVerification
                ///           is set to required in the request.
                ///         - Verifies that the client JSON is of type "webauthn.get", i.e. the client was responding to a request to
                ///           assert authentication.
                ///         - Verifies that the client JSON contains the requested challenge.
                ///         - Verifies that (r, s) constitute a valid signature over both the authenicatorData and client JSON, for public
                ///            key (x, y).
                ///
                ///      We make some assumptions about the particular use case of this verifier, so we do NOT verify the following:
                ///         - Does NOT verify that the origin in the `clientDataJSON` matches the Relying Party's origin: tt is considered
                ///           the authenticator's responsibility to ensure that the user is interacting with the correct RP. This is
                ///           enforced by most high quality authenticators properly, particularly the iCloud Keychain and Google Password
                ///           Manager were tested.
                ///         - Does NOT verify That `topOrigin` in `clientDataJSON` is well-formed: We assume it would never be present, i.e.
                ///           the credentials are never used in a cross-origin/iframe context. The website/app set up should disallow
                ///           cross-origin usage of the credentials. This is the default behaviour for created credentials in common settings.
                ///         - Does NOT verify that the `rpIdHash` in `authenticatorData` is the SHA-256 hash of the RP ID expected by the Relying
                ///           Party: this means that we rely on the authenticator to properly enforce credentials to be used only by the correct RP.
                ///           This is generally enforced with features like Apple App Site Association and Google Asset Links. To protect from
                ///           edge cases in which a previously-linked RP ID is removed from the authorised RP IDs, we recommend that messages
                ///           signed by the authenticator include some expiry mechanism.
                ///         - Does NOT verify the credential backup state: this assumes the credential backup state is NOT used as part of Relying
                ///           Party business logic or policy.
                ///         - Does NOT verify the values of the client extension outputs: this assumes that the Relying Party does not use client
                ///           extension outputs.
                ///         - Does NOT verify the signature counter: signature counters are intended to enable risk scoring for the Relying Party.
                ///           This assumes risk scoring is not used as part of Relying Party business logic or policy.
                ///         - Does NOT verify the attestation object: this assumes that response.attestationObject is NOT present in the response,
                ///           i.e. the RP does not intend to verify an attestation.
                ///
                /// @param challenge    The challenge that was provided by the relying party.
                /// @param requireUV    A boolean indicating whether user verification is required.
                /// @param webAuthnAuth The `WebAuthnAuth` struct.
                /// @param x            The x coordinate of the public key.
                /// @param y            The y coordinate of the public key.
                ///
                /// @return `true` if the authentication assertion passed validation, else `false`.
                function verify(bytes memory challenge, bool requireUV, WebAuthnAuth memory webAuthnAuth, uint256 x, uint256 y)
                    internal
                    view
                    returns (bool)
                {
                    if (webAuthnAuth.s > _P256_N_DIV_2) {
                        // guard against signature malleability
                        return false;
                    }
                    // 11. Verify that the value of C.type is the string webauthn.get.
                    //     bytes("type":"webauthn.get").length = 21
                    string memory _type = webAuthnAuth.clientDataJSON.slice(webAuthnAuth.typeIndex, webAuthnAuth.typeIndex + 21);
                    if (keccak256(bytes(_type)) != _EXPECTED_TYPE_HASH) {
                        return false;
                    }
                    // 12. Verify that the value of C.challenge equals the base64url encoding of options.challenge.
                    bytes memory expectedChallenge = bytes(string.concat('"challenge":"', Base64.encodeURL(challenge), '"'));
                    string memory actualChallenge =
                        webAuthnAuth.clientDataJSON.slice(webAuthnAuth.challengeIndex, webAuthnAuth.challengeIndex + expectedChallenge.length);
                    if (keccak256(bytes(actualChallenge)) != keccak256(expectedChallenge)) {
                        return false;
                    }
                    // Skip 13., 14., 15.
                    // 16. Verify that the UP bit of the flags in authData is set.
                    if (webAuthnAuth.authenticatorData[32] & _AUTH_DATA_FLAGS_UP != _AUTH_DATA_FLAGS_UP) {
                        return false;
                    }
                    // 17. If user verification is required for this assertion, verify that the User Verified bit of the flags in
                    //     authData is set.
                    if (requireUV && (webAuthnAuth.authenticatorData[32] & _AUTH_DATA_FLAGS_UV) != _AUTH_DATA_FLAGS_UV) {
                        return false;
                    }
                    // skip 18.
                    // 19. Let hash be the result of computing a hash over the cData using SHA-256.
                    bytes32 clientDataJSONHash = sha256(bytes(webAuthnAuth.clientDataJSON));
                    // 20. Using credentialPublicKey, verify that sig is a valid signature over the binary concatenation of authData
                    //     and hash.
                    bytes32 messageHash = sha256(abi.encodePacked(webAuthnAuth.authenticatorData, clientDataJSONHash));
                    bytes memory args = abi.encode(messageHash, webAuthnAuth.r, webAuthnAuth.s, x, y);
                    // try the RIP-7212 precompile address
                    (bool success, bytes memory ret) = _VERIFIER.staticcall(args);
                    // staticcall will not revert if address has no code
                    // check return length
                    // note that even if precompile exists, ret.length is 0 when verification returns false
                    // so an invalid signature will be checked twice: once by the precompile and once by FCL.
                    // Ideally this signature failure is simulated offchain and no one actually pay this gas.
                    bool valid = ret.length > 0;
                    if (success && valid) return abi.decode(ret, (uint256)) == 1;
                    return FCL_ecdsa.ecdsa_verify(messageHash, webAuthnAuth.r, webAuthnAuth.s, x, y);
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.4;
            /// @title ERC-1271
            ///
            /// @notice Abstract ERC-1271 implementation (based on Solady's) with guards to handle the same
            ///         signer being used on multiple accounts.
            ///
            /// @dev To prevent the same signature from being validated on different accounts owned by the samer signer,
            ///      we introduce an anti cross-account-replay layer: the original hash is input into a new EIP-712 compliant
            ///      hash. The domain separator of this outer hash contains the chain id and address of this contract, so that
            ///      it cannot be used on two accounts (see `replaySafeHash()` for the implementation details).
            ///
            /// @author Coinbase (https://github.com/coinbase/smart-wallet)
            /// @author Solady (https://github.com/vectorized/solady/blob/main/src/accounts/ERC1271.sol)
            abstract contract ERC1271 {
                /// @dev Precomputed `typeHash` used to produce EIP-712 compliant hash when applying the anti
                ///      cross-account-replay layer.
                ///
                ///      The original hash must either be:
                ///         - An EIP-191 hash: keccak256("\\x19Ethereum Signed Message:\
            " || len(someMessage) || someMessage)
                ///         - An EIP-712 hash: keccak256("\\x19\\x01" || someDomainSeparator || hashStruct(someStruct))
                bytes32 private constant _MESSAGE_TYPEHASH = keccak256("CoinbaseSmartWalletMessage(bytes32 hash)");
                /// @notice Returns information about the `EIP712Domain` used to create EIP-712 compliant hashes.
                ///
                /// @dev Follows ERC-5267 (see https://eips.ethereum.org/EIPS/eip-5267).
                ///
                /// @return fields The bitmap of used fields.
                /// @return name The value of the `EIP712Domain.name` field.
                /// @return version The value of the `EIP712Domain.version` field.
                /// @return chainId The value of the `EIP712Domain.chainId` field.
                /// @return verifyingContract The value of the `EIP712Domain.verifyingContract` field.
                /// @return salt The value of the `EIP712Domain.salt` field.
                /// @return extensions The list of EIP numbers, that extends EIP-712 with new domain fields.
                function eip712Domain()
                    external
                    view
                    virtual
                    returns (
                        bytes1 fields,
                        string memory name,
                        string memory version,
                        uint256 chainId,
                        address verifyingContract,
                        bytes32 salt,
                        uint256[] memory extensions
                    )
                {
                    fields = hex"0f"; // `0b1111`.
                    (name, version) = _domainNameAndVersion();
                    chainId = block.chainid;
                    verifyingContract = address(this);
                    salt = salt; // `bytes32(0)`.
                    extensions = extensions; // `new uint256[](0)`.
                }
                /// @notice Validates the `signature` against the given `hash`.
                ///
                /// @dev This implementation follows ERC-1271. See https://eips.ethereum.org/EIPS/eip-1271.
                /// @dev IMPORTANT: Signature verification is performed on the hash produced AFTER applying the anti
                ///      cross-account-replay layer on the given `hash` (i.e., verification is run on the replay-safe
                ///      hash version).
                ///
                /// @param hash      The original hash.
                /// @param signature The signature of the replay-safe hash to validate.
                ///
                /// @return result `0x1626ba7e` if validation succeeded, else `0xffffffff`.
                function isValidSignature(bytes32 hash, bytes calldata signature) public view virtual returns (bytes4 result) {
                    if (_isValidSignature({hash: replaySafeHash(hash), signature: signature})) {
                        // bytes4(keccak256("isValidSignature(bytes32,bytes)"))
                        return 0x1626ba7e;
                    }
                    return 0xffffffff;
                }
                /// @notice Wrapper around `_eip712Hash()` to produce a replay-safe hash fron the given `hash`.
                ///
                /// @dev The returned EIP-712 compliant replay-safe hash is the result of:
                ///      keccak256(
                ///         \\x19\\x01 ||
                ///         this.domainSeparator ||
                ///         hashStruct(CoinbaseSmartWalletMessage({ hash: `hash`}))
                ///      )
                ///
                /// @param hash The original hash.
                ///
                /// @return The corresponding replay-safe hash.
                function replaySafeHash(bytes32 hash) public view virtual returns (bytes32) {
                    return _eip712Hash(hash);
                }
                /// @notice Returns the `domainSeparator` used to create EIP-712 compliant hashes.
                ///
                /// @dev Implements domainSeparator = hashStruct(eip712Domain).
                ///      See https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator.
                ///
                /// @return The 32 bytes domain separator result.
                function domainSeparator() public view returns (bytes32) {
                    (string memory name, string memory version) = _domainNameAndVersion();
                    return keccak256(
                        abi.encode(
                            keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                            keccak256(bytes(name)),
                            keccak256(bytes(version)),
                            block.chainid,
                            address(this)
                        )
                    );
                }
                /// @notice Returns the EIP-712 typed hash of the `CoinbaseSmartWalletMessage(bytes32 hash)` data structure.
                ///
                /// @dev Implements encode(domainSeparator : 𝔹²⁵⁶, message : 𝕊) = "\\x19\\x01" || domainSeparator ||
                ///      hashStruct(message).
                /// @dev See https://eips.ethereum.org/EIPS/eip-712#specification.
                ///
                /// @param hash The `CoinbaseSmartWalletMessage.hash` field to hash.
                ////
                /// @return The resulting EIP-712 hash.
                function _eip712Hash(bytes32 hash) internal view virtual returns (bytes32) {
                    return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator(), _hashStruct(hash)));
                }
                /// @notice Returns the EIP-712 `hashStruct` result of the `CoinbaseSmartWalletMessage(bytes32 hash)` data
                ///         structure.
                ///
                /// @dev Implements hashStruct(s : 𝕊) = keccak256(typeHash || encodeData(s)).
                /// @dev See https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
                ///
                /// @param hash The `CoinbaseSmartWalletMessage.hash` field.
                ///
                /// @return The EIP-712 `hashStruct` result.
                function _hashStruct(bytes32 hash) internal view virtual returns (bytes32) {
                    return keccak256(abi.encode(_MESSAGE_TYPEHASH, hash));
                }
                /// @notice Returns the domain name and version to use when creating EIP-712 signatures.
                ///
                /// @dev MUST be defined by the implementation.
                ///
                /// @return name    The user readable name of signing domain.
                /// @return version The current major version of the signing domain.
                function _domainNameAndVersion() internal view virtual returns (string memory name, string memory version);
                /// @notice Validates the `signature` against the given `hash`.
                ///
                /// @dev MUST be defined by the implementation.
                ///
                /// @param hash      The hash whose signature has been performed on.
                /// @param signature The signature associated with `hash`.
                ///
                /// @return `true` is the signature is valid, else `false`.
                function _isValidSignature(bytes32 hash, bytes calldata signature) internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.18;
            /// @notice Storage layout used by this contract.
            ///
            /// @custom:storage-location erc7201:coinbase.storage.MultiOwnable
            struct MultiOwnableStorage {
                /// @dev Tracks the index of the next owner to add.
                uint256 nextOwnerIndex;
                /// @dev Tracks number of owners that have been removed.
                uint256 removedOwnersCount;
                /// @dev Maps index to owner bytes, used to idenfitied owners via a uint256 index.
                ///
                ///      Some uses—-such as signature validation for secp256r1 public key owners—-
                ///      requires the caller to assert the public key of the caller. To economize calldata,
                ///      we allow an index to identify an owner, so that the full owner bytes do
                ///      not need to be passed.
                ///
                ///      The `owner` bytes should either be
                ///         - An ABI encoded Ethereum address
                ///         - An ABI encoded public key
                mapping(uint256 index => bytes owner) ownerAtIndex;
                /// @dev Mapping of bytes to booleans indicating whether or not
                ///      bytes_ is an owner of this contract.
                mapping(bytes bytes_ => bool isOwner_) isOwner;
            }
            /// @title Multi Ownable
            ///
            /// @notice Auth contract allowing multiple owners, each identified as bytes.
            ///
            /// @author Coinbase (https://github.com/coinbase/smart-wallet)
            contract MultiOwnable {
                /// @dev Slot for the `MultiOwnableStorage` struct in storage.
                ///      Computed from
                ///      keccak256(abi.encode(uint256(keccak256("coinbase.storage.MultiOwnable")) - 1)) & ~bytes32(uint256(0xff))
                ///      Follows ERC-7201 (see https://eips.ethereum.org/EIPS/eip-7201).
                bytes32 private constant MUTLI_OWNABLE_STORAGE_LOCATION =
                    0x97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f00;
                /// @notice Thrown when the `msg.sender` is not an owner and is trying to call a privileged function.
                error Unauthorized();
                /// @notice Thrown when trying to add an already registered owner.
                ///
                /// @param owner The owner bytes.
                error AlreadyOwner(bytes owner);
                /// @notice Thrown when trying to remove an owner from an index that is empty.
                ///
                /// @param index The targeted index for removal.
                error NoOwnerAtIndex(uint256 index);
                /// @notice Thrown when `owner` argument does not match owner found at index.
                ///
                /// @param index         The index of the owner to be removed.
                /// @param expectedOwner The owner passed in the remove call.
                /// @param actualOwner   The actual owner at `index`.
                error WrongOwnerAtIndex(uint256 index, bytes expectedOwner, bytes actualOwner);
                /// @notice Thrown when a provided owner is neither 64 bytes long (for public key)
                ///         nor a ABI encoded address.
                ///
                /// @param owner The invalid owner.
                error InvalidOwnerBytesLength(bytes owner);
                /// @notice Thrown if a provided owner is 32 bytes long but does not fit in an `address` type.
                ///
                /// @param owner The invalid owner.
                error InvalidEthereumAddressOwner(bytes owner);
                /// @notice Thrown when removeOwnerAtIndex is called and there is only one current owner.
                error LastOwner();
                /// @notice Thrown when removeLastOwner is called and there is more than one current owner.
                ///
                /// @param ownersRemaining The number of current owners.
                error NotLastOwner(uint256 ownersRemaining);
                /// @notice Emitted when a new owner is registered.
                ///
                /// @param index The owner index of the owner added.
                /// @param owner The owner added.
                event AddOwner(uint256 indexed index, bytes owner);
                /// @notice Emitted when an owner is removed.
                ///
                /// @param index The owner index of the owner removed.
                /// @param owner The owner removed.
                event RemoveOwner(uint256 indexed index, bytes owner);
                /// @notice Access control modifier ensuring the caller is an authorized owner
                modifier onlyOwner() virtual {
                    _checkOwner();
                    _;
                }
                /// @notice Adds a new Ethereum-address owner.
                ///
                /// @param owner The owner address.
                function addOwnerAddress(address owner) external virtual onlyOwner {
                    _addOwnerAtIndex(abi.encode(owner), _getMultiOwnableStorage().nextOwnerIndex++);
                }
                /// @notice Adds a new public-key owner.
                ///
                /// @param x The owner public key x coordinate.
                /// @param y The owner public key y coordinate.
                function addOwnerPublicKey(bytes32 x, bytes32 y) external virtual onlyOwner {
                    _addOwnerAtIndex(abi.encode(x, y), _getMultiOwnableStorage().nextOwnerIndex++);
                }
                /// @notice Removes owner at the given `index`.
                ///
                /// @dev Reverts if the owner is not registered at `index`.
                /// @dev Reverts if there is currently only one owner.
                /// @dev Reverts if `owner` does not match bytes found at `index`.
                ///
                /// @param index The index of the owner to be removed.
                /// @param owner The ABI encoded bytes of the owner to be removed.
                function removeOwnerAtIndex(uint256 index, bytes calldata owner) external virtual onlyOwner {
                    if (ownerCount() == 1) {
                        revert LastOwner();
                    }
                    _removeOwnerAtIndex(index, owner);
                }
                /// @notice Removes owner at the given `index`, which should be the only current owner.
                ///
                /// @dev Reverts if the owner is not registered at `index`.
                /// @dev Reverts if there is currently more than one owner.
                /// @dev Reverts if `owner` does not match bytes found at `index`.
                ///
                /// @param index The index of the owner to be removed.
                /// @param owner The ABI encoded bytes of the owner to be removed.
                function removeLastOwner(uint256 index, bytes calldata owner) external virtual onlyOwner {
                    uint256 ownersRemaining = ownerCount();
                    if (ownersRemaining > 1) {
                        revert NotLastOwner(ownersRemaining);
                    }
                    _removeOwnerAtIndex(index, owner);
                }
                /// @notice Checks if the given `account` address is registered as owner.
                ///
                /// @param account The account address to check.
                ///
                /// @return `true` if the account is an owner else `false`.
                function isOwnerAddress(address account) public view virtual returns (bool) {
                    return _getMultiOwnableStorage().isOwner[abi.encode(account)];
                }
                /// @notice Checks if the given `x`, `y` public key is registered as owner.
                ///
                /// @param x The public key x coordinate.
                /// @param y The public key y coordinate.
                ///
                /// @return `true` if the account is an owner else `false`.
                function isOwnerPublicKey(bytes32 x, bytes32 y) public view virtual returns (bool) {
                    return _getMultiOwnableStorage().isOwner[abi.encode(x, y)];
                }
                /// @notice Checks if the given `account` bytes is registered as owner.
                ///
                /// @param account The account, should be ABI encoded address or public key.
                ///
                /// @return `true` if the account is an owner else `false`.
                function isOwnerBytes(bytes memory account) public view virtual returns (bool) {
                    return _getMultiOwnableStorage().isOwner[account];
                }
                /// @notice Returns the owner bytes at the given `index`.
                ///
                /// @param index The index to lookup.
                ///
                /// @return The owner bytes (empty if no owner is registered at this `index`).
                function ownerAtIndex(uint256 index) public view virtual returns (bytes memory) {
                    return _getMultiOwnableStorage().ownerAtIndex[index];
                }
                /// @notice Returns the next index that will be used to add a new owner.
                ///
                /// @return The next index that will be used to add a new owner.
                function nextOwnerIndex() public view virtual returns (uint256) {
                    return _getMultiOwnableStorage().nextOwnerIndex;
                }
                /// @notice Returns the current number of owners
                ///
                /// @return The current owner count
                function ownerCount() public view virtual returns (uint256) {
                    MultiOwnableStorage storage $ = _getMultiOwnableStorage();
                    return $.nextOwnerIndex - $.removedOwnersCount;
                }
                /// @notice Tracks the number of owners removed
                ///
                /// @dev Used with `this.nextOwnerIndex` to avoid removing all owners
                ///
                /// @return The number of owners that have been removed.
                function removedOwnersCount() public view virtual returns (uint256) {
                    return _getMultiOwnableStorage().removedOwnersCount;
                }
                /// @notice Initialize the owners of this contract.
                ///
                /// @dev Intended to be called contract is first deployed and never again.
                /// @dev Reverts if a provided owner is neither 64 bytes long (for public key) nor a valid address.
                ///
                /// @param owners The initial set of owners.
                function _initializeOwners(bytes[] memory owners) internal virtual {
                    MultiOwnableStorage storage $ = _getMultiOwnableStorage();
                    uint256 nextOwnerIndex_ = $.nextOwnerIndex;
                    for (uint256 i; i < owners.length; i++) {
                        if (owners[i].length != 32 && owners[i].length != 64) {
                            revert InvalidOwnerBytesLength(owners[i]);
                        }
                        if (owners[i].length == 32 && uint256(bytes32(owners[i])) > type(uint160).max) {
                            revert InvalidEthereumAddressOwner(owners[i]);
                        }
                        _addOwnerAtIndex(owners[i], nextOwnerIndex_++);
                    }
                    $.nextOwnerIndex = nextOwnerIndex_;
                }
                /// @notice Adds an owner at the given `index`.
                ///
                /// @dev Reverts if `owner` is already registered as an owner.
                ///
                /// @param owner The owner raw bytes to register.
                /// @param index The index to write to.
                function _addOwnerAtIndex(bytes memory owner, uint256 index) internal virtual {
                    if (isOwnerBytes(owner)) revert AlreadyOwner(owner);
                    MultiOwnableStorage storage $ = _getMultiOwnableStorage();
                    $.isOwner[owner] = true;
                    $.ownerAtIndex[index] = owner;
                    emit AddOwner(index, owner);
                }
                /// @notice Removes owner at the given `index`.
                ///
                /// @dev Reverts if the owner is not registered at `index`.
                /// @dev Reverts if `owner` does not match bytes found at `index`.
                ///
                /// @param index The index of the owner to be removed.
                /// @param owner The ABI encoded bytes of the owner to be removed.
                function _removeOwnerAtIndex(uint256 index, bytes calldata owner) internal virtual {
                    bytes memory owner_ = ownerAtIndex(index);
                    if (owner_.length == 0) revert NoOwnerAtIndex(index);
                    if (keccak256(owner_) != keccak256(owner)) {
                        revert WrongOwnerAtIndex({index: index, expectedOwner: owner, actualOwner: owner_});
                    }
                    MultiOwnableStorage storage $ = _getMultiOwnableStorage();
                    delete $.isOwner[owner];
                    delete $.ownerAtIndex[index];
                    $.removedOwnersCount++;
                    emit RemoveOwner(index, owner);
                }
                /// @notice Checks if the sender is an owner of this contract or the contract itself.
                ///
                /// @dev Revert if the sender is not an owner fo the contract itself.
                function _checkOwner() internal view virtual {
                    if (isOwnerAddress(msg.sender) || (msg.sender == address(this))) {
                        return;
                    }
                    revert Unauthorized();
                }
                /// @notice Helper function to get a storage reference to the `MultiOwnableStorage` struct.
                ///
                /// @return $ A storage reference to the `MultiOwnableStorage` struct.
                function _getMultiOwnableStorage() internal pure returns (MultiOwnableStorage storage $) {
                    assembly ("memory-safe") {
                        $.slot := MUTLI_OWNABLE_STORAGE_LOCATION
                    }
                }
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.8.12;
            /* solhint-disable no-inline-assembly */
            /**
             * returned data from validateUserOp.
             * validateUserOp returns a uint256, with is created by `_packedValidationData` and parsed by `_parseValidationData`
             * @param aggregator - address(0) - the account validated the signature by itself.
             *              address(1) - the account failed to validate the signature.
             *              otherwise - this is an address of a signature aggregator that must be used to validate the signature.
             * @param validAfter - this UserOp is valid only after this timestamp.
             * @param validaUntil - this UserOp is valid only up to this timestamp.
             */
                struct ValidationData {
                    address aggregator;
                    uint48 validAfter;
                    uint48 validUntil;
                }
            //extract sigFailed, validAfter, validUntil.
            // also convert zero validUntil to type(uint48).max
                function _parseValidationData(uint validationData) pure returns (ValidationData memory data) {
                    address aggregator = address(uint160(validationData));
                    uint48 validUntil = uint48(validationData >> 160);
                    if (validUntil == 0) {
                        validUntil = type(uint48).max;
                    }
                    uint48 validAfter = uint48(validationData >> (48 + 160));
                    return ValidationData(aggregator, validAfter, validUntil);
                }
            // intersect account and paymaster ranges.
                function _intersectTimeRange(uint256 validationData, uint256 paymasterValidationData) pure returns (ValidationData memory) {
                    ValidationData memory accountValidationData = _parseValidationData(validationData);
                    ValidationData memory pmValidationData = _parseValidationData(paymasterValidationData);
                    address aggregator = accountValidationData.aggregator;
                    if (aggregator == address(0)) {
                        aggregator = pmValidationData.aggregator;
                    }
                    uint48 validAfter = accountValidationData.validAfter;
                    uint48 validUntil = accountValidationData.validUntil;
                    uint48 pmValidAfter = pmValidationData.validAfter;
                    uint48 pmValidUntil = pmValidationData.validUntil;
                    if (validAfter < pmValidAfter) validAfter = pmValidAfter;
                    if (validUntil > pmValidUntil) validUntil = pmValidUntil;
                    return ValidationData(aggregator, validAfter, validUntil);
                }
            /**
             * helper to pack the return value for validateUserOp
             * @param data - the ValidationData to pack
             */
                function _packValidationData(ValidationData memory data) pure returns (uint256) {
                    return uint160(data.aggregator) | (uint256(data.validUntil) << 160) | (uint256(data.validAfter) << (160 + 48));
                }
            /**
             * helper to pack the return value for validateUserOp, when not using an aggregator
             * @param sigFailed - true for signature failure, false for success
             * @param validUntil last timestamp this UserOperation is valid (or zero for infinite)
             * @param validAfter first timestamp this UserOperation is valid
             */
                function _packValidationData(bool sigFailed, uint48 validUntil, uint48 validAfter) pure returns (uint256) {
                    return (sigFailed ? 1 : 0) | (uint256(validUntil) << 160) | (uint256(validAfter) << (160 + 48));
                }
            /**
             * keccak function over calldata.
             * @dev copy calldata into memory, do keccak and drop allocated memory. Strangely, this is more efficient than letting solidity do it.
             */
                function calldataKeccak(bytes calldata data) pure returns (bytes32 ret) {
                    assembly {
                        let mem := mload(0x40)
                        let len := data.length
                        calldatacopy(mem, data.offset, len)
                        ret := keccak256(mem, len)
                    }
                }
            //********************************************************************************************/
            //  ___           _       ___               _         _    _ _
            // | __| _ ___ __| |_    / __|_ _ _  _ _ __| |_ ___  | |  (_) |__
            // | _| '_/ -_|_-< ' \\  | (__| '_| || | '_ \\  _/ _ \\ | |__| | '_ \\
            // |_||_| \\___/__/_||_|  \\___|_|  \\_, | .__/\\__\\___/ |____|_|_.__/
            //                                |__/|_|
            ///* Copyright (C) 2022 - Renaud Dubois - This file is part of FCL (Fresh CryptoLib) project
            ///* License: This software is licensed under MIT License
            ///* This Code may be reused including license and copyright notice.
            ///* See LICENSE file at the root folder of the project.
            ///* FILE: FCL_ecdsa.sol
            ///*
            ///*
            ///* DESCRIPTION: ecdsa verification implementation
            ///*
            //**************************************************************************************/
            //* WARNING: this code SHALL not be used for non prime order curves for security reasons.
            // Code is optimized for a=-3 only curves with prime order, constant like -1, -2 shall be replaced
            // if ever used for other curve than sec256R1
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.8.19 <0.9.0;
            import {FCL_Elliptic_ZZ} from "./FCL_elliptic.sol";
            library FCL_ecdsa {
                // Set parameters for curve sec256r1.public
                  //curve order (number of points)
                uint256 constant n = FCL_Elliptic_ZZ.n;
              
                /**
                 * @dev ECDSA verification, given , signature, and public key.
                 */
                /**
                 * @dev ECDSA verification, given , signature, and public key, no calldata version
                 */
                function ecdsa_verify(bytes32 message, uint256 r, uint256 s, uint256 Qx, uint256 Qy)  internal view returns (bool){
                    if (r == 0 || r >= FCL_Elliptic_ZZ.n || s == 0 || s >= FCL_Elliptic_ZZ.n) {
                        return false;
                    }
                    
                    if (!FCL_Elliptic_ZZ.ecAff_isOnCurve(Qx, Qy)) {
                        return false;
                    }
                    uint256 sInv = FCL_Elliptic_ZZ.FCL_nModInv(s);
                    uint256 scalar_u = mulmod(uint256(message), sInv, FCL_Elliptic_ZZ.n);
                    uint256 scalar_v = mulmod(r, sInv, FCL_Elliptic_ZZ.n);
                    uint256 x1;
                    x1 = FCL_Elliptic_ZZ.ecZZ_mulmuladd_S_asm(Qx, Qy, scalar_u, scalar_v);
                    x1= addmod(x1, n-r,n );
                
                    return x1 == 0;
                }
                function ec_recover_r1(uint256 h, uint256 v, uint256 r, uint256 s) internal view returns (address)
                {
                     if (r == 0 || r >= FCL_Elliptic_ZZ.n || s == 0 || s >= FCL_Elliptic_ZZ.n) {
                        return address(0);
                    }
                    uint256 y=FCL_Elliptic_ZZ.ec_Decompress(r, v-27);
                    uint256 rinv=FCL_Elliptic_ZZ.FCL_nModInv(r);
                    uint256 u1=mulmod(FCL_Elliptic_ZZ.n-addmod(0,h,FCL_Elliptic_ZZ.n), rinv,FCL_Elliptic_ZZ.n);//-hr^-1
                    uint256 u2=mulmod(s, rinv,FCL_Elliptic_ZZ.n);//sr^-1
                    uint256 Qx;
                    uint256 Qy;
                    (Qx,Qy)=FCL_Elliptic_ZZ.ecZZ_mulmuladd(r,y, u1, u2);
                    return address(uint160(uint256(keccak256(abi.encodePacked(Qx, Qy)))));
                }
                function ecdsa_precomputed_verify(bytes32 message, uint256 r, uint256 s, address Shamir8)
                    internal view
                    returns (bool)
                {
                   
                    if (r == 0 || r >= n || s == 0 || s >= n) {
                        return false;
                    }
                    /* Q is pushed via the contract at address Shamir8 assumed to be correct
                    if (!isOnCurve(Q[0], Q[1])) {
                        return false;
                    }*/
                    uint256 sInv = FCL_Elliptic_ZZ.FCL_nModInv(s);
                    uint256 X;
                    //Shamir 8 dimensions
                    X = FCL_Elliptic_ZZ.ecZZ_mulmuladd_S8_extcode(mulmod(uint256(message), sInv, n), mulmod(r, sInv, n), Shamir8);
                    X= addmod(X, n-r,n );
                    return X == 0;
                } //end  ecdsa_precomputed_verify()
                 function ecdsa_precomputed_verify(bytes32 message, uint256[2] calldata rs, address Shamir8)
                    internal view
                    returns (bool)
                {
                    uint256 r = rs[0];
                    uint256 s = rs[1];
                    if (r == 0 || r >= n || s == 0 || s >= n) {
                        return false;
                    }
                    /* Q is pushed via the contract at address Shamir8 assumed to be correct
                    if (!isOnCurve(Q[0], Q[1])) {
                        return false;
                    }*/
                    uint256 sInv = FCL_Elliptic_ZZ.FCL_nModInv(s);
                    uint256 X;
                    //Shamir 8 dimensions
                    X = FCL_Elliptic_ZZ.ecZZ_mulmuladd_S8_extcode(mulmod(uint256(message), sInv, n), mulmod(r, sInv, n), Shamir8);
                    X= addmod(X, n-r,n );
                    return X == 0;
                } //end  ecdsa_precomputed_verify()
            }
            //********************************************************************************************/
            //  ___           _       ___               _         _    _ _
            // | __| _ ___ __| |_    / __|_ _ _  _ _ __| |_ ___  | |  (_) |__
            // | _| '_/ -_|_-< ' \\  | (__| '_| || | '_ \\  _/ _ \\ | |__| | '_ \\
            // |_||_| \\___/__/_||_|  \\___|_|  \\_, | .__/\\__\\___/ |____|_|_.__/
            //                                |__/|_|
            ///* Copyright (C) 2022 - Renaud Dubois - This file is part of FCL (Fresh CryptoLib) project
            ///* License: This software is licensed under MIT License
            ///* This Code may be reused including license and copyright notice.
            ///* See LICENSE file at the root folder of the project.
            ///* FILE: FCL_elliptic.sol
            ///*
            ///*
            ///* DESCRIPTION: modified XYZZ system coordinates for EVM elliptic point multiplication
            ///*  optimization
            ///*
            //**************************************************************************************/
            //* WARNING: this code SHALL not be used for non prime order curves for security reasons.
            // Code is optimized for a=-3 only curves with prime order, constant like -1, -2 shall be replaced
            // if ever used for other curve than sec256R1
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.8.19 <0.9.0;
            library FCL_Elliptic_ZZ {
                // Set parameters for curve sec256r1.
                // address of the ModExp precompiled contract (Arbitrary-precision exponentiation under modulo)
                address constant MODEXP_PRECOMPILE = 0x0000000000000000000000000000000000000005;
                //curve prime field modulus
                uint256 constant p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF;
                //short weierstrass first coefficient
                uint256 constant a = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC;
                //short weierstrass second coefficient
                uint256 constant b = 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B;
                //generating point affine coordinates
                uint256 constant gx = 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296;
                uint256 constant gy = 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5;
                //curve order (number of points)
                uint256 constant n = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551;
                /* -2 mod p constant, used to speed up inversion and doubling (avoid negation)*/
                uint256 constant minus_2 = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFD;
                /* -2 mod n constant, used to speed up inversion*/
                uint256 constant minus_2modn = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254F;
                uint256 constant minus_1 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
                //P+1 div 4
                uint256 constant pp1div4=0x3fffffffc0000000400000000000000000000000400000000000000000000000;
                //arbitrary constant to express no quadratic residuosity
                uint256 constant _NOTSQUARE=0xFFFFFFFF00000002000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF;
                uint256 constant _NOTONCURVE=0xFFFFFFFF00000003000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF;
                /**
                 * /* inversion mod n via a^(n-2), use of precompiled using little Fermat theorem
                 */
                function FCL_nModInv(uint256 u) internal view returns (uint256 result) {
                    assembly {
                        let pointer := mload(0x40)
                        // Define length of base, exponent and modulus. 0x20 == 32 bytes
                        mstore(pointer, 0x20)
                        mstore(add(pointer, 0x20), 0x20)
                        mstore(add(pointer, 0x40), 0x20)
                        // Define variables base, exponent and modulus
                        mstore(add(pointer, 0x60), u)
                        mstore(add(pointer, 0x80), minus_2modn)
                        mstore(add(pointer, 0xa0), n)
                        // Call the precompiled contract 0x05 = ModExp
                        if iszero(staticcall(not(0), 0x05, pointer, 0xc0, pointer, 0x20)) { revert(0, 0) }
                        result := mload(pointer)
                    }
                }
                /**
                 * /* @dev inversion mod nusing little Fermat theorem via a^(n-2), use of precompiled
                 */
                function FCL_pModInv(uint256 u) internal view returns (uint256 result) {
                    assembly {
                        let pointer := mload(0x40)
                        // Define length of base, exponent and modulus. 0x20 == 32 bytes
                        mstore(pointer, 0x20)
                        mstore(add(pointer, 0x20), 0x20)
                        mstore(add(pointer, 0x40), 0x20)
                        // Define variables base, exponent and modulus
                        mstore(add(pointer, 0x60), u)
                        mstore(add(pointer, 0x80), minus_2)
                        mstore(add(pointer, 0xa0), p)
                        // Call the precompiled contract 0x05 = ModExp
                        if iszero(staticcall(not(0), 0x05, pointer, 0xc0, pointer, 0x20)) { revert(0, 0) }
                        result := mload(pointer)
                    }
                }
                //Coron projective shuffling, take as input alpha as blinding factor
               function ecZZ_Coronize(uint256 alpha, uint256 x, uint256 y,  uint256 zz, uint256 zzz) internal pure  returns (uint256 x3, uint256 y3, uint256 zz3, uint256 zzz3)
               {
                   
                    uint256 alpha2=mulmod(alpha,alpha,p);
                   
                    x3=mulmod(alpha2, x,p); //alpha^-2.x
                    y3=mulmod(mulmod(alpha, alpha2,p), y,p);
                    zz3=mulmod(zz,alpha2,p);//alpha^2 zz
                    zzz3=mulmod(zzz,mulmod(alpha, alpha2,p),p);//alpha^3 zzz
                    
                    return (x3, y3, zz3, zzz3);
               }
             function ecZZ_Add(uint256 x1, uint256 y1, uint256 zz1, uint256 zzz1, uint256 x2, uint256 y2, uint256 zz2, uint256 zzz2) internal pure  returns (uint256 x3, uint256 y3, uint256 zz3, uint256 zzz3)
              {
                uint256 u1=mulmod(x1,zz2,p); // U1 = X1*ZZ2
                uint256 u2=mulmod(x2, zz1,p);               //  U2 = X2*ZZ1
                u2=addmod(u2, p-u1, p);//  P = U2-U1
                x1=mulmod(u2, u2, p);//PP
                x2=mulmod(x1, u2, p);//PPP
                
                zz3=mulmod(x1, mulmod(zz1, zz2, p),p);//ZZ3 = ZZ1*ZZ2*PP  
                zzz3=mulmod(zzz1, mulmod(zzz2, x2, p),p);//ZZZ3 = ZZZ1*ZZZ2*PPP
                zz1=mulmod(y1, zzz2,p);  // S1 = Y1*ZZZ2
                zz2=mulmod(y2, zzz1, p);    // S2 = Y2*ZZZ1 
                zz2=addmod(zz2, p-zz1, p);//R = S2-S1
                zzz1=mulmod(u1, x1,p); //Q = U1*PP
                x3= addmod(addmod(mulmod(zz2, zz2, p), p-x2,p), mulmod(minus_2, zzz1,p),p); //X3 = R2-PPP-2*Q
                y3=addmod( mulmod(zz2, addmod(zzz1, p-x3, p),p), p-mulmod(zz1, x2, p),p);//R*(Q-X3)-S1*PPP
                return (x3, y3, zz3, zzz3);
              }
            /// @notice Calculate one modular square root of a given integer. Assume that p=3 mod 4.
            /// @dev Uses the ModExp precompiled contract at address 0x05 for fast computation using little Fermat theorem
            /// @param self The integer of which to find the modular inverse
            /// @return result The modular inverse of the input integer. If the modular inverse doesn't exist, it revert the tx
            function SqrtMod(uint256 self) internal view returns (uint256 result){
             assembly ("memory-safe") {
                    // load the free memory pointer value
                    let pointer := mload(0x40)
                    // Define length of base (Bsize)
                    mstore(pointer, 0x20)
                    // Define the exponent size (Esize)
                    mstore(add(pointer, 0x20), 0x20)
                    // Define the modulus size (Msize)
                    mstore(add(pointer, 0x40), 0x20)
                    // Define variables base (B)
                    mstore(add(pointer, 0x60), self)
                    // Define the exponent (E)
                    mstore(add(pointer, 0x80), pp1div4)
                    // We save the point of the last argument, it will be override by the result
                    // of the precompile call in order to avoid paying for the memory expansion properly
                    let _result := add(pointer, 0xa0)
                    // Define the modulus (M)
                    mstore(_result, p)
                    // Call the precompiled ModExp (0x05) https://www.evm.codes/precompiled#0x05
                    if iszero(
                        staticcall(
                            not(0), // amount of gas to send
                            MODEXP_PRECOMPILE, // target
                            pointer, // argsOffset
                            0xc0, // argsSize (6 * 32 bytes)
                            _result, // retOffset (we override M to avoid paying for the memory expansion)
                            0x20 // retSize (32 bytes)
                        )
                    ) { revert(0, 0) }
              result := mload(_result)
            //  result :=addmod(result,0,p)
             }
               if(mulmod(result,result,p)!=self){
                 result=_NOTSQUARE;
               }
              
               return result;
            }
                /**
                 * /* @dev Convert from affine rep to XYZZ rep
                 */
                function ecAff_SetZZ(uint256 x0, uint256 y0) internal pure returns (uint256[4] memory P) {
                    unchecked {
                        P[2] = 1; //ZZ
                        P[3] = 1; //ZZZ
                        P[0] = x0;
                        P[1] = y0;
                    }
                }
                function ec_Decompress(uint256 x, uint256 parity) internal view returns(uint256 y){ 
                    uint256 y2=mulmod(x,mulmod(x,x,p),p);//x3
                    y2=addmod(b,addmod(y2,mulmod(x,a,p),p),p);//x3+ax+b
                    y=SqrtMod(y2);
                    if(y==_NOTSQUARE){
                       return _NOTONCURVE;
                    }
                    if((y&1)!=(parity&1)){
                        y=p-y;
                    }
                }
                /**
                 * /* @dev Convert from XYZZ rep to affine rep
                 */
                /*    https://hyperelliptic.org/EFD/g1p/auto-shortw-xyzz-3.html#addition-add-2008-s*/
                function ecZZ_SetAff(uint256 x, uint256 y, uint256 zz, uint256 zzz) internal view returns (uint256 x1, uint256 y1) {
                    uint256 zzzInv = FCL_pModInv(zzz); //1/zzz
                    y1 = mulmod(y, zzzInv, p); //Y/zzz
                    uint256 _b = mulmod(zz, zzzInv, p); //1/z
                    zzzInv = mulmod(_b, _b, p); //1/zz
                    x1 = mulmod(x, zzzInv, p); //X/zz
                }
                /**
                 * /* @dev Sutherland2008 doubling
                 */
                /* The "dbl-2008-s-1" doubling formulas */
                function ecZZ_Dbl(uint256 x, uint256 y, uint256 zz, uint256 zzz)
                    internal
                    pure
                    returns (uint256 P0, uint256 P1, uint256 P2, uint256 P3)
                {
                    unchecked {
                        assembly {
                            P0 := mulmod(2, y, p) //U = 2*Y1
                            P2 := mulmod(P0, P0, p) // V=U^2
                            P3 := mulmod(x, P2, p) // S = X1*V
                            P1 := mulmod(P0, P2, p) // W=UV
                            P2 := mulmod(P2, zz, p) //zz3=V*ZZ1
                            zz := mulmod(3, mulmod(addmod(x, sub(p, zz), p), addmod(x, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1)
                            P0 := addmod(mulmod(zz, zz, p), mulmod(minus_2, P3, p), p) //X3=M^2-2S
                            x := mulmod(zz, addmod(P3, sub(p, P0), p), p) //M(S-X3)
                            P3 := mulmod(P1, zzz, p) //zzz3=W*zzz1
                            P1 := addmod(x, sub(p, mulmod(P1, y, p)), p) //Y3= M(S-X3)-W*Y1
                        }
                    }
                    return (P0, P1, P2, P3);
                }
                /**
                 * @dev Sutherland2008 add a ZZ point with a normalized point and greedy formulae
                 * warning: assume that P1(x1,y1)!=P2(x2,y2), true in multiplication loop with prime order (cofactor 1)
                 */
                function ecZZ_AddN(uint256 x1, uint256 y1, uint256 zz1, uint256 zzz1, uint256 x2, uint256 y2)
                    internal
                    pure
                    returns (uint256 P0, uint256 P1, uint256 P2, uint256 P3)
                {
                    unchecked {
                        if (y1 == 0) {
                            return (x2, y2, 1, 1);
                        }
                        assembly {
                            y1 := sub(p, y1)
                            y2 := addmod(mulmod(y2, zzz1, p), y1, p)
                            x2 := addmod(mulmod(x2, zz1, p), sub(p, x1), p)
                            P0 := mulmod(x2, x2, p) //PP = P^2
                            P1 := mulmod(P0, x2, p) //PPP = P*PP
                            P2 := mulmod(zz1, P0, p) ////ZZ3 = ZZ1*PP
                            P3 := mulmod(zzz1, P1, p) ////ZZZ3 = ZZZ1*PPP
                            zz1 := mulmod(x1, P0, p) //Q = X1*PP
                            P0 := addmod(addmod(mulmod(y2, y2, p), sub(p, P1), p), mulmod(minus_2, zz1, p), p) //R^2-PPP-2*Q
                            P1 := addmod(mulmod(addmod(zz1, sub(p, P0), p), y2, p), mulmod(y1, P1, p), p) //R*(Q-X3)
                        }
                        //end assembly
                    } //end unchecked
                    return (P0, P1, P2, P3);
                }
                /**
                 * @dev Return the zero curve in XYZZ coordinates.
                 */
                function ecZZ_SetZero() internal pure returns (uint256 x, uint256 y, uint256 zz, uint256 zzz) {
                    return (0, 0, 0, 0);
                }
                /**
                 * @dev Check if point is the neutral of the curve
                 */
                // uint256 x0, uint256 y0, uint256 zz0, uint256 zzz0
                function ecZZ_IsZero(uint256, uint256 y0, uint256, uint256) internal pure returns (bool) {
                    return y0 == 0;
                }
                /**
                 * @dev Return the zero curve in affine coordinates. Compatible with the double formulae (no special case)
                 */
                function ecAff_SetZero() internal pure returns (uint256 x, uint256 y) {
                    return (0, 0);
                }
                /**
                 * @dev Check if the curve is the zero curve in affine rep.
                 */
                // uint256 x, uint256 y)
                function ecAff_IsZero(uint256, uint256 y) internal pure returns (bool flag) {
                    return (y == 0);
                }
                /**
                 * @dev Check if a point in affine coordinates is on the curve (reject Neutral that is indeed on the curve).
                 */
                function ecAff_isOnCurve(uint256 x, uint256 y) internal pure returns (bool) {
                    if (x >= p || y >= p || ((x == 0) && (y == 0))) {
                        return false;
                    }
                    unchecked {
                        uint256 LHS = mulmod(y, y, p); // y^2
                        uint256 RHS = addmod(mulmod(mulmod(x, x, p), x, p), mulmod(x, a, p), p); // x^3+ax
                        RHS = addmod(RHS, b, p); // x^3 + a*x + b
                        return LHS == RHS;
                    }
                }
                /**
                 * @dev Add two elliptic curve points in affine coordinates. Deal with P=Q
                 */
                function ecAff_add(uint256 x0, uint256 y0, uint256 x1, uint256 y1) internal view returns (uint256, uint256) {
                    uint256 zz0;
                    uint256 zzz0;
                    if (ecAff_IsZero(x0, y0)) return (x1, y1);
                    if (ecAff_IsZero(x1, y1)) return (x0, y0);
                    if((x0==x1)&&(y0==y1)) {
                        (x0, y0, zz0, zzz0) = ecZZ_Dbl(x0, y0,1,1);
                    }
                    else{
                        (x0, y0, zz0, zzz0) = ecZZ_AddN(x0, y0, 1, 1, x1, y1);
                    }
                    return ecZZ_SetAff(x0, y0, zz0, zzz0);
                }
                /**
                 * @dev Computation of uG+vQ using Strauss-Shamir's trick, G basepoint, Q public key
                 *       Returns only x for ECDSA use            
                 *      */
                function ecZZ_mulmuladd_S_asm(
                    uint256 Q0,
                    uint256 Q1, //affine rep for input point Q
                    uint256 scalar_u,
                    uint256 scalar_v
                ) internal view returns (uint256 X) {
                    uint256 zz;
                    uint256 zzz;
                    uint256 Y;
                    uint256 index = 255;
                    uint256 H0;
                    uint256 H1;
                    unchecked {
                        if (scalar_u == 0 && scalar_v == 0) return 0;
                        (H0, H1) = ecAff_add(gx, gy, Q0, Q1); 
                        if((H0==0)&&(H1==0))//handling Q=-G
                        {
                            scalar_u=addmod(scalar_u, n-scalar_v, n);
                            scalar_v=0;
                            if (scalar_u == 0 && scalar_v == 0) return 0;
                        }
                        assembly {
                            for { let T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1)) } eq(T4, 0) {
                                index := sub(index, 1)
                                T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1))
                            } {}
                            zz := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1))
                            if eq(zz, 1) {
                                X := gx
                                Y := gy
                            }
                            if eq(zz, 2) {
                                X := Q0
                                Y := Q1
                            }
                            if eq(zz, 3) {
                                X := H0
                                Y := H1
                            }
                            index := sub(index, 1)
                            zz := 1
                            zzz := 1
                            for {} gt(minus_1, index) { index := sub(index, 1) } {
                                // inlined EcZZ_Dbl
                                let T1 := mulmod(2, Y, p) //U = 2*Y1, y free
                                let T2 := mulmod(T1, T1, p) // V=U^2
                                let T3 := mulmod(X, T2, p) // S = X1*V
                                T1 := mulmod(T1, T2, p) // W=UV
                                let T4 := mulmod(3, mulmod(addmod(X, sub(p, zz), p), addmod(X, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1)
                                zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1
                                zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free
                                X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S
                                T2 := mulmod(T4, addmod(X, sub(p, T3), p), p) //-M(S-X3)=M(X3-S)
                                Y := addmod(mulmod(T1, Y, p), T2, p) //-Y3= W*Y1-M(S-X3), we replace Y by -Y to avoid a sub in ecAdd
                                {
                                    //value of dibit
                                    T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1))
                                    if iszero(T4) {
                                        Y := sub(p, Y) //restore the -Y inversion
                                        continue
                                    } // if T4!=0
                                    if eq(T4, 1) {
                                        T1 := gx
                                        T2 := gy
                                    }
                                    if eq(T4, 2) {
                                        T1 := Q0
                                        T2 := Q1
                                    }
                                    if eq(T4, 3) {
                                        T1 := H0
                                        T2 := H1
                                    }
                                    if iszero(zz) {
                                        X := T1
                                        Y := T2
                                        zz := 1
                                        zzz := 1
                                        continue
                                    }
                                    // inlined EcZZ_AddN
                                    //T3:=sub(p, Y)
                                    //T3:=Y
                                    let y2 := addmod(mulmod(T2, zzz, p), Y, p) //R
                                    T2 := addmod(mulmod(T1, zz, p), sub(p, X), p) //P
                                    //special extremely rare case accumulator where EcAdd is replaced by EcDbl, no need to optimize this
                                    //todo : construct edge vector case
                                    if iszero(y2) {
                                        if iszero(T2) {
                                            T1 := mulmod(minus_2, Y, p) //U = 2*Y1, y free
                                            T2 := mulmod(T1, T1, p) // V=U^2
                                            T3 := mulmod(X, T2, p) // S = X1*V
                                            T1 := mulmod(T1, T2, p) // W=UV
                                            y2 := mulmod(addmod(X, zz, p), addmod(X, sub(p, zz), p), p) //(X-ZZ)(X+ZZ)
                                            T4 := mulmod(3, y2, p) //M=3*(X-ZZ)(X+ZZ)
                                            zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1
                                            zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free
                                            X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S
                                            T2 := mulmod(T4, addmod(T3, sub(p, X), p), p) //M(S-X3)
                                            Y := addmod(T2, mulmod(T1, Y, p), p) //Y3= M(S-X3)-W*Y1
                                            continue
                                        }
                                    }
                                    T4 := mulmod(T2, T2, p) //PP
                                    let TT1 := mulmod(T4, T2, p) //PPP, this one could be spared, but adding this register spare gas
                                    zz := mulmod(zz, T4, p)
                                    zzz := mulmod(zzz, TT1, p) //zz3=V*ZZ1
                                    let TT2 := mulmod(X, T4, p)
                                    T4 := addmod(addmod(mulmod(y2, y2, p), sub(p, TT1), p), mulmod(minus_2, TT2, p), p)
                                    Y := addmod(mulmod(addmod(TT2, sub(p, T4), p), y2, p), mulmod(Y, TT1, p), p)
                                    X := T4
                                }
                            } //end loop
                            let T := mload(0x40)
                            mstore(add(T, 0x60), zz)
                            //(X,Y)=ecZZ_SetAff(X,Y,zz, zzz);
                            //T[0] = inverseModp_Hard(T[0], p); //1/zzz, inline modular inversion using precompile:
                            // Define length of base, exponent and modulus. 0x20 == 32 bytes
                            mstore(T, 0x20)
                            mstore(add(T, 0x20), 0x20)
                            mstore(add(T, 0x40), 0x20)
                            // Define variables base, exponent and modulus
                            //mstore(add(pointer, 0x60), u)
                            mstore(add(T, 0x80), minus_2)
                            mstore(add(T, 0xa0), p)
                            // Call the precompiled contract 0x05 = ModExp
                            if iszero(staticcall(not(0), 0x05, T, 0xc0, T, 0x20)) { revert(0, 0) }
                            //Y:=mulmod(Y,zzz,p)//Y/zzz
                            //zz :=mulmod(zz, mload(T),p) //1/z
                            //zz:= mulmod(zz,zz,p) //1/zz
                            X := mulmod(X, mload(T), p) //X/zz
                        } //end assembly
                    } //end unchecked
                    return X;
                }
                /**
                 * @dev Computation of uG+vQ using Strauss-Shamir's trick, G basepoint, Q public key
                 *       Returns affine representation of point (normalized)       
                 *      */
                function ecZZ_mulmuladd(
                    uint256 Q0,
                    uint256 Q1, //affine rep for input point Q
                    uint256 scalar_u,
                    uint256 scalar_v
                ) internal view returns (uint256 X, uint256 Y) {
                    uint256 zz;
                    uint256 zzz;
                    uint256 index = 255;
                    uint256[6] memory T;
                    uint256[2] memory H;
             
                    unchecked {
                        if (scalar_u == 0 && scalar_v == 0) return (0,0);
                        (H[0], H[1]) = ecAff_add(gx, gy, Q0, Q1); //will not work if Q=P, obvious forbidden private key
                        assembly {
                            for { let T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1)) } eq(T4, 0) {
                                index := sub(index, 1)
                                T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1))
                            } {}
                            zz := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1))
                            if eq(zz, 1) {
                                X := gx
                                Y := gy
                            }
                            if eq(zz, 2) {
                                X := Q0
                                Y := Q1
                            }
                            if eq(zz, 3) {
                                Y := mload(add(H,32))
                                X := mload(H)
                            }
                            index := sub(index, 1)
                            zz := 1
                            zzz := 1
                            for {} gt(minus_1, index) { index := sub(index, 1) } {
                                // inlined EcZZ_Dbl
                                let T1 := mulmod(2, Y, p) //U = 2*Y1, y free
                                let T2 := mulmod(T1, T1, p) // V=U^2
                                let T3 := mulmod(X, T2, p) // S = X1*V
                                T1 := mulmod(T1, T2, p) // W=UV
                                let T4 := mulmod(3, mulmod(addmod(X, sub(p, zz), p), addmod(X, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1)
                                zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1
                                zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free
                                X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S
                                T2 := mulmod(T4, addmod(X, sub(p, T3), p), p) //-M(S-X3)=M(X3-S)
                                Y := addmod(mulmod(T1, Y, p), T2, p) //-Y3= W*Y1-M(S-X3), we replace Y by -Y to avoid a sub in ecAdd
                                {
                                    //value of dibit
                                    T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1))
                                    if iszero(T4) {
                                        Y := sub(p, Y) //restore the -Y inversion
                                        continue
                                    } // if T4!=0
                                    if eq(T4, 1) {
                                        T1 := gx
                                        T2 := gy
                                    }
                                    if eq(T4, 2) {
                                        T1 := Q0
                                        T2 := Q1
                                    }
                                    if eq(T4, 3) {
                                        T1 := mload(H)
                                        T2 := mload(add(H,32))
                                    }
                                    if iszero(zz) {
                                        X := T1
                                        Y := T2
                                        zz := 1
                                        zzz := 1
                                        continue
                                    }
                                    // inlined EcZZ_AddN
                                    //T3:=sub(p, Y)
                                    //T3:=Y
                                    let y2 := addmod(mulmod(T2, zzz, p), Y, p) //R
                                    T2 := addmod(mulmod(T1, zz, p), sub(p, X), p) //P
                                    //special extremely rare case accumulator where EcAdd is replaced by EcDbl, no need to optimize this
                                    //todo : construct edge vector case
                                    if iszero(y2) {
                                        if iszero(T2) {
                                            T1 := mulmod(minus_2, Y, p) //U = 2*Y1, y free
                                            T2 := mulmod(T1, T1, p) // V=U^2
                                            T3 := mulmod(X, T2, p) // S = X1*V
                                            T1 := mulmod(T1, T2, p) // W=UV
                                            y2 := mulmod(addmod(X, zz, p), addmod(X, sub(p, zz), p), p) //(X-ZZ)(X+ZZ)
                                            T4 := mulmod(3, y2, p) //M=3*(X-ZZ)(X+ZZ)
                                            zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1
                                            zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free
                                            X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S
                                            T2 := mulmod(T4, addmod(T3, sub(p, X), p), p) //M(S-X3)
                                            Y := addmod(T2, mulmod(T1, Y, p), p) //Y3= M(S-X3)-W*Y1
                                            continue
                                        }
                                    }
                                    T4 := mulmod(T2, T2, p) //PP
                                    let TT1 := mulmod(T4, T2, p) //PPP, this one could be spared, but adding this register spare gas
                                    zz := mulmod(zz, T4, p)
                                    zzz := mulmod(zzz, TT1, p) //zz3=V*ZZ1
                                    let TT2 := mulmod(X, T4, p)
                                    T4 := addmod(addmod(mulmod(y2, y2, p), sub(p, TT1), p), mulmod(minus_2, TT2, p), p)
                                    Y := addmod(mulmod(addmod(TT2, sub(p, T4), p), y2, p), mulmod(Y, TT1, p), p)
                                    X := T4
                                }
                            } //end loop
                            mstore(add(T, 0x60), zzz)
                            //(X,Y)=ecZZ_SetAff(X,Y,zz, zzz);
                            //T[0] = inverseModp_Hard(T[0], p); //1/zzz, inline modular inversion using precompile:
                            // Define length of base, exponent and modulus. 0x20 == 32 bytes
                            mstore(T, 0x20)
                            mstore(add(T, 0x20), 0x20)
                            mstore(add(T, 0x40), 0x20)
                            // Define variables base, exponent and modulus
                            //mstore(add(pointer, 0x60), u)
                            mstore(add(T, 0x80), minus_2)
                            mstore(add(T, 0xa0), p)
                            // Call the precompiled contract 0x05 = ModExp
                            if iszero(staticcall(not(0), 0x05, T, 0xc0, T, 0x20)) { revert(0, 0) }
                            Y:=mulmod(Y,mload(T),p)//Y/zzz
                            zz :=mulmod(zz, mload(T),p) //1/z
                            zz:= mulmod(zz,zz,p) //1/zz
                            X := mulmod(X, zz, p) //X/zz
                        } //end assembly
                    } //end unchecked
                    return (X,Y);
                }
                //8 dimensions Shamir's trick, using precomputations stored in Shamir8,  stored as Bytecode of an external
                //contract at given address dataPointer
                //(thx to Lakhdar https://github.com/Kelvyne for EVM storage explanations and tricks)
                // the external tool to generate tables from public key is in the /sage directory
                function ecZZ_mulmuladd_S8_extcode(uint256 scalar_u, uint256 scalar_v, address dataPointer)
                    internal view
                    returns (uint256 X /*, uint Y*/ )
                {
                    unchecked {
                        uint256 zz; // third and  coordinates of the point
                        uint256[6] memory T;
                        zz = 256; //start index
                        while (T[0] == 0) {
                            zz = zz - 1;
                            //tbd case of msb octobit is null
                            T[0] = 64
                                * (
                                    128 * ((scalar_v >> zz) & 1) + 64 * ((scalar_v >> (zz - 64)) & 1)
                                        + 32 * ((scalar_v >> (zz - 128)) & 1) + 16 * ((scalar_v >> (zz - 192)) & 1)
                                        + 8 * ((scalar_u >> zz) & 1) + 4 * ((scalar_u >> (zz - 64)) & 1)
                                        + 2 * ((scalar_u >> (zz - 128)) & 1) + ((scalar_u >> (zz - 192)) & 1)
                                );
                        }
                        assembly {
                            extcodecopy(dataPointer, T, mload(T), 64)
                            let index := sub(zz, 1)
                            X := mload(T)
                            let Y := mload(add(T, 32))
                            let zzz := 1
                            zz := 1
                            //loop over 1/4 of scalars thx to Shamir's trick over 8 points
                            for {} gt(index, 191) { index := add(index, 191) } {
                                //inline Double
                                {
                                    let TT1 := mulmod(2, Y, p) //U = 2*Y1, y free
                                    let T2 := mulmod(TT1, TT1, p) // V=U^2
                                    let T3 := mulmod(X, T2, p) // S = X1*V
                                    let T1 := mulmod(TT1, T2, p) // W=UV
                                    let T4 := mulmod(3, mulmod(addmod(X, sub(p, zz), p), addmod(X, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1)
                                    zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1
                                    zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free
                                    X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S
                                    //T2:=mulmod(T4,addmod(T3, sub(p, X),p),p)//M(S-X3)
                                    let T5 := mulmod(T4, addmod(X, sub(p, T3), p), p) //-M(S-X3)=M(X3-S)
                                    //Y:= addmod(T2, sub(p, mulmod(T1, Y ,p)),p  )//Y3= M(S-X3)-W*Y1
                                    Y := addmod(mulmod(T1, Y, p), T5, p) //-Y3= W*Y1-M(S-X3), we replace Y by -Y to avoid a sub in ecAdd
                                    /* compute element to access in precomputed table */
                                }
                                {
                                    let T4 := add(shl(13, and(shr(index, scalar_v), 1)), shl(9, and(shr(index, scalar_u), 1)))
                                    let index2 := sub(index, 64)
                                    let T3 :=
                                        add(T4, add(shl(12, and(shr(index2, scalar_v), 1)), shl(8, and(shr(index2, scalar_u), 1))))
                                    let index3 := sub(index2, 64)
                                    let T2 :=
                                        add(T3, add(shl(11, and(shr(index3, scalar_v), 1)), shl(7, and(shr(index3, scalar_u), 1))))
                                    index := sub(index3, 64)
                                    let T1 :=
                                        add(T2, add(shl(10, and(shr(index, scalar_v), 1)), shl(6, and(shr(index, scalar_u), 1))))
                                    //tbd: check validity of formulae with (0,1) to remove conditional jump
                                    if iszero(T1) {
                                        Y := sub(p, Y)
                                        continue
                                    }
                                    extcodecopy(dataPointer, T, T1, 64)
                                }
                                {
                                    /* Access to precomputed table using extcodecopy hack */
                                    // inlined EcZZ_AddN
                                    if iszero(zz) {
                                        X := mload(T)
                                        Y := mload(add(T, 32))
                                        zz := 1
                                        zzz := 1
                                        continue
                                    }
                                    let y2 := addmod(mulmod(mload(add(T, 32)), zzz, p), Y, p)
                                    let T2 := addmod(mulmod(mload(T), zz, p), sub(p, X), p)
                                    //special case ecAdd(P,P)=EcDbl
                                    if iszero(y2) {
                                        if iszero(T2) {
                                            let T1 := mulmod(minus_2, Y, p) //U = 2*Y1, y free
                                            T2 := mulmod(T1, T1, p) // V=U^2
                                            let T3 := mulmod(X, T2, p) // S = X1*V
                                            T1 := mulmod(T1, T2, p) // W=UV
                                            y2 := mulmod(addmod(X, zz, p), addmod(X, sub(p, zz), p), p) //(X-ZZ)(X+ZZ)
                                            let T4 := mulmod(3, y2, p) //M=3*(X-ZZ)(X+ZZ)
                                            zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1
                                            zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free
                                            X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S
                                            T2 := mulmod(T4, addmod(T3, sub(p, X), p), p) //M(S-X3)
                                            Y := addmod(T2, mulmod(T1, Y, p), p) //Y3= M(S-X3)-W*Y1
                                            continue
                                        }
                                    }
                                    let T4 := mulmod(T2, T2, p)
                                    let T1 := mulmod(T4, T2, p) //
                                    zz := mulmod(zz, T4, p)
                                    //zzz3=V*ZZ1
                                    zzz := mulmod(zzz, T1, p) // W=UV/
                                    let zz1 := mulmod(X, T4, p)
                                    X := addmod(addmod(mulmod(y2, y2, p), sub(p, T1), p), mulmod(minus_2, zz1, p), p)
                                    Y := addmod(mulmod(addmod(zz1, sub(p, X), p), y2, p), mulmod(Y, T1, p), p)
                                }
                            } //end loop
                            mstore(add(T, 0x60), zz)
                            //(X,Y)=ecZZ_SetAff(X,Y,zz, zzz);
                            //T[0] = inverseModp_Hard(T[0], p); //1/zzz, inline modular inversion using precompile:
                            // Define length of base, exponent and modulus. 0x20 == 32 bytes
                            mstore(T, 0x20)
                            mstore(add(T, 0x20), 0x20)
                            mstore(add(T, 0x40), 0x20)
                            // Define variables base, exponent and modulus
                            //mstore(add(pointer, 0x60), u)
                            mstore(add(T, 0x80), minus_2)
                            mstore(add(T, 0xa0), p)
                            // Call the precompiled contract 0x05 = ModExp
                            if iszero(staticcall(not(0), 0x05, T, 0xc0, T, 0x20)) { revert(0, 0) }
                            zz := mload(T)
                            X := mulmod(X, zz, p) //X/zz
                        }
                    } //end unchecked
                }
               
                // improving the extcodecopy trick : append array at end of contract
                function ecZZ_mulmuladd_S8_hackmem(uint256 scalar_u, uint256 scalar_v, uint256 dataPointer)
                    internal view
                    returns (uint256 X /*, uint Y*/ )
                {
                    uint256 zz; // third and  coordinates of the point
                    uint256[6] memory T;
                    zz = 256; //start index
                    unchecked {
                        while (T[0] == 0) {
                            zz = zz - 1;
                            //tbd case of msb octobit is null
                            T[0] = 64
                                * (
                                    128 * ((scalar_v >> zz) & 1) + 64 * ((scalar_v >> (zz - 64)) & 1)
                                        + 32 * ((scalar_v >> (zz - 128)) & 1) + 16 * ((scalar_v >> (zz - 192)) & 1)
                                        + 8 * ((scalar_u >> zz) & 1) + 4 * ((scalar_u >> (zz - 64)) & 1)
                                        + 2 * ((scalar_u >> (zz - 128)) & 1) + ((scalar_u >> (zz - 192)) & 1)
                                );
                        }
                        assembly {
                            codecopy(T, add(mload(T), dataPointer), 64)
                            X := mload(T)
                            let Y := mload(add(T, 32))
                            let zzz := 1
                            zz := 1
                            //loop over 1/4 of scalars thx to Shamir's trick over 8 points
                            for { let index := 254 } gt(index, 191) { index := add(index, 191) } {
                                let T1 := mulmod(2, Y, p) //U = 2*Y1, y free
                                let T2 := mulmod(T1, T1, p) // V=U^2
                                let T3 := mulmod(X, T2, p) // S = X1*V
                                T1 := mulmod(T1, T2, p) // W=UV
                                let T4 := mulmod(3, mulmod(addmod(X, sub(p, zz), p), addmod(X, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1)
                                zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1
                                zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free
                                X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S
                                //T2:=mulmod(T4,addmod(T3, sub(p, X),p),p)//M(S-X3)
                                T2 := mulmod(T4, addmod(X, sub(p, T3), p), p) //-M(S-X3)=M(X3-S)
                                //Y:= addmod(T2, sub(p, mulmod(T1, Y ,p)),p  )//Y3= M(S-X3)-W*Y1
                                Y := addmod(mulmod(T1, Y, p), T2, p) //-Y3= W*Y1-M(S-X3), we replace Y by -Y to avoid a sub in ecAdd
                                /* compute element to access in precomputed table */
                                T4 := add(shl(13, and(shr(index, scalar_v), 1)), shl(9, and(shr(index, scalar_u), 1)))
                                index := sub(index, 64)
                                T4 := add(T4, add(shl(12, and(shr(index, scalar_v), 1)), shl(8, and(shr(index, scalar_u), 1))))
                                index := sub(index, 64)
                                T4 := add(T4, add(shl(11, and(shr(index, scalar_v), 1)), shl(7, and(shr(index, scalar_u), 1))))
                                index := sub(index, 64)
                                T4 := add(T4, add(shl(10, and(shr(index, scalar_v), 1)), shl(6, and(shr(index, scalar_u), 1))))
                                //index:=add(index,192), restore index, interleaved with loop
                                //tbd: check validity of formulae with (0,1) to remove conditional jump
                                if iszero(T4) {
                                    Y := sub(p, Y)
                                    continue
                                }
                                {
                                    /* Access to precomputed table using extcodecopy hack */
                                    codecopy(T, add(T4, dataPointer), 64)
                                    // inlined EcZZ_AddN
                                    let y2 := addmod(mulmod(mload(add(T, 32)), zzz, p), Y, p)
                                    T2 := addmod(mulmod(mload(T), zz, p), sub(p, X), p)
                                    T4 := mulmod(T2, T2, p)
                                    T1 := mulmod(T4, T2, p)
                                    T2 := mulmod(zz, T4, p) // W=UV
                                    zzz := mulmod(zzz, T1, p) //zz3=V*ZZ1
                                    let zz1 := mulmod(X, T4, p)
                                    T4 := addmod(addmod(mulmod(y2, y2, p), sub(p, T1), p), mulmod(minus_2, zz1, p), p)
                                    Y := addmod(mulmod(addmod(zz1, sub(p, T4), p), y2, p), mulmod(Y, T1, p), p)
                                    zz := T2
                                    X := T4
                                }
                            } //end loop
                            mstore(add(T, 0x60), zz)
                            //(X,Y)=ecZZ_SetAff(X,Y,zz, zzz);
                            //T[0] = inverseModp_Hard(T[0], p); //1/zzz, inline modular inversion using precompile:
                            // Define length of base, exponent and modulus. 0x20 == 32 bytes
                            mstore(T, 0x20)
                            mstore(add(T, 0x20), 0x20)
                            mstore(add(T, 0x40), 0x20)
                            // Define variables base, exponent and modulus
                            //mstore(add(pointer, 0x60), u)
                            mstore(add(T, 0x80), minus_2)
                            mstore(add(T, 0xa0), p)
                            // Call the precompiled contract 0x05 = ModExp
                            if iszero(staticcall(not(0), 0x05, T, 0xc0, T, 0x20)) { revert(0, 0) }
                            zz := mload(T)
                            X := mulmod(X, zz, p) //X/zz
                        }
                    } //end unchecked
                }
                /**
                 * @dev ECDSA verification using a precomputed table of multiples of P and Q stored in contract at address Shamir8
                 *     generation of contract bytecode for precomputations is done using sagemath code
                 *     (see sage directory, WebAuthn_precompute.sage)
                 */
                /**
                 * @dev ECDSA verification using a precomputed table of multiples of P and Q appended at end of contract at address endcontract
                 *     generation of contract bytecode for precomputations is done using sagemath code
                 *     (see sage directory, WebAuthn_precompute.sage)
                 */
                function ecdsa_precomputed_hackmem(bytes32 message, uint256[2] calldata rs, uint256 endcontract)
                    internal view
                    returns (bool)
                {
                    uint256 r = rs[0];
                    uint256 s = rs[1];
                    if (r == 0 || r >= n || s == 0 || s >= n) {
                        return false;
                    }
                    /* Q is pushed via bytecode assumed to be correct
                    if (!isOnCurve(Q[0], Q[1])) {
                        return false;
                    }*/
                    uint256 sInv = FCL_nModInv(s);
                    uint256 X;
                    //Shamir 8 dimensions
                    X = ecZZ_mulmuladd_S8_hackmem(mulmod(uint256(message), sInv, n), mulmod(r, sInv, n), endcontract);
                    assembly {
                        X := addmod(X, sub(n, r), n)
                    }
                    return X == 0;
                } //end  ecdsa_precomputed_verify()
            } //EOF
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v5.0.2) (utils/Base64.sol)
            pragma solidity ^0.8.20;
            /**
             * @dev Provides a set of functions to operate with Base64 strings.
             */
            library Base64 {
                /**
                 * @dev Base64 Encoding/Decoding Table
                 * See sections 4 and 5 of https://datatracker.ietf.org/doc/html/rfc4648
                 */
                string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
                string internal constant _TABLE_URL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
                /**
                 * @dev Converts a `bytes` to its Bytes64 `string` representation.
                 */
                function encode(bytes memory data) internal pure returns (string memory) {
                    return _encode(data, _TABLE, true);
                }
                /**
                 * @dev Converts a `bytes` to its Bytes64Url `string` representation.
                 */
                function encodeURL(bytes memory data) internal pure returns (string memory) {
                    return _encode(data, _TABLE_URL, false);
                }
                /**
                 * @dev Internal table-agnostic conversion
                 */
                function _encode(bytes memory data, string memory table, bool withPadding) private pure returns (string memory) {
                    /**
                     * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
                     * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
                     */
                    if (data.length == 0) return "";
                    // If padding is enabled, the final length should be `bytes` data length divided by 3 rounded up and then
                    // multiplied by 4 so that it leaves room for padding the last chunk
                    // - `data.length + 2`  -> Round up
                    // - `/ 3`              -> Number of 3-bytes chunks
                    // - `4 *`              -> 4 characters for each chunk
                    // If padding is disabled, the final length should be `bytes` data length multiplied by 4/3 rounded up as
                    // opposed to when padding is required to fill the last chunk.
                    // - `4 *`              -> 4 characters for each chunk
                    // - `data.length + 2`  -> Round up
                    // - `/ 3`              -> Number of 3-bytes chunks
                    uint256 resultLength = withPadding ? 4 * ((data.length + 2) / 3) : (4 * data.length + 2) / 3;
                    string memory result = new string(resultLength);
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Prepare the lookup table (skip the first "length" byte)
                        let tablePtr := add(table, 1)
                        // Prepare result pointer, jump over length
                        let resultPtr := add(result, 0x20)
                        let dataPtr := data
                        let endPtr := add(data, mload(data))
                        // In some cases, the last iteration will read bytes after the end of the data. We cache the value, and
                        // set it to zero to make sure no dirty bytes are read in that section.
                        let afterPtr := add(endPtr, 0x20)
                        let afterCache := mload(afterPtr)
                        mstore(afterPtr, 0x00)
                        // Run over the input, 3 bytes at a time
                        for {
                        } lt(dataPtr, endPtr) {
                        } {
                            // Advance 3 bytes
                            dataPtr := add(dataPtr, 3)
                            let input := mload(dataPtr)
                            // To write each character, shift the 3 byte (24 bits) chunk
                            // 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
                            // and apply logical AND with 0x3F to bitmask the least significant 6 bits.
                            // Use this as an index into the lookup table, mload an entire word
                            // so the desired character is in the least significant byte, and
                            // mstore8 this least significant byte into the result and continue.
                            mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
                            resultPtr := add(resultPtr, 1) // Advance
                            mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
                            resultPtr := add(resultPtr, 1) // Advance
                            mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
                            resultPtr := add(resultPtr, 1) // Advance
                            mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
                            resultPtr := add(resultPtr, 1) // Advance
                        }
                        // Reset the value that was cached
                        mstore(afterPtr, afterCache)
                        if withPadding {
                            // When data `bytes` is not exactly 3 bytes long
                            // it is padded with `=` characters at the end
                            switch mod(mload(data), 3)
                            case 1 {
                                mstore8(sub(resultPtr, 1), 0x3d)
                                mstore8(sub(resultPtr, 2), 0x3d)
                            }
                            case 2 {
                                mstore8(sub(resultPtr, 1), 0x3d)
                            }
                        }
                    }
                    return result;
                }
            }
            // 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)
                    }
                }
            }