ETH Price: $2,190.00 (-9.66%)

Transaction Decoder

Block:
22745078 at Jun-20-2025 10:25:47 AM +UTC
Transaction Fee:
0.000245247279066343 ETH $0.54
Gas Used:
274,343 Gas / 0.893944001 Gwei

Emitted Events:

618 TransparentUpgradeableProxy.0x956cd63ee4cdcd81fda5f0ec7c6c36dceda99e1b412f4a650a5d26055dc3c450( 0x956cd63ee4cdcd81fda5f0ec7c6c36dceda99e1b412f4a650a5d26055dc3c450, 5261100442297d5833d54bb4b0229fa126ae79d5ba1d37c92b7e546e8e05820e, 4908a6f5edc86366f37fcc8ffd9627e536ee2ce706a9805e8b09bbbb60b4765b, 0000000000000000000000000000000000000000000000000000000000000001, 000000000000000000000000000000000000000000000000021570a125066000 )
619 WETH9.Transfer( src=0x60f759C538a16DE173a0bA48533c952Ed541A7FE, dst=TransparentUpgradeableProxy, wad=750750000000000 )
620 WETH9.Transfer( src=0x60f759C538a16DE173a0bA48533c952Ed541A7FE, dst=0x4bBf570791B989152CFa7EC4dAEF5d14C494C806, wad=7507500000000000 )
621 WETH9.Transfer( src=0x60f759C538a16DE173a0bA48533c952Ed541A7FE, dst=Proxy, wad=14264250000000000 )
622 WETH9.Transfer( src=0x60f759C538a16DE173a0bA48533c952Ed541A7FE, dst=[Sender] 0x020c811f28441166127c67517101bd9ad15ef433, wad=135135000000000000 )
623 DropERC721.Approval( owner=[Sender] 0x020c811f28441166127c67517101bd9ad15ef433, approved=0x00000000...000000000, tokenId=79 )
624 DropERC721.Transfer( from=[Sender] 0x020c811f28441166127c67517101bd9ad15ef433, to=0x60f759C538a16DE173a0bA48533c952Ed541A7FE, tokenId=79 )

Account State Difference:

  Address   Before After State Difference Code
0x020C811f...ad15ef433
0.140616244700724551 Eth
Nonce: 660
0.140370997421658208 Eth
Nonce: 661
0.000245247279066343
(Titan Builder)
7.645357185389596645 Eth7.645357185390145331 Eth0.000000000000548686
0x7E5Ca132...68C9Aa263
0x9757F2d2...4107cd8D6
(Rarible: Exchange V2)
0xC02aaA39...83C756Cc2

Execution Trace

TransparentUpgradeableProxy.e99a3f80( )
  • ExchangeV2.matchOrders( orderLeft=[{name:maker, type:address, order:1, indexed:false, value:0x60f759C538a16DE173a0bA48533c952Ed541A7FE, valueString:0x60f759C538a16DE173a0bA48533c952Ed541A7FE}, {name:makeAsset, type:tuple, order:2, indexed:false, value:[{name:assetType, type:tuple, order:1, indexed:false, value:[{name:assetClass, type:bytes4, order:1, indexed:false, value:iuhdhA==, valueString:System.Byte[]}, {name:data, type:bytes, order:2, indexed:false, value:0x000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2, valueString:0x000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2}], valueString:[{name:assetClass, type:bytes4, order:1, indexed:false, value:iuhdhA==, valueString:System.Byte[]}, {name:data, type:bytes, order:2, indexed:false, value:0x000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2, valueString:0x000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2}]}, {name:value, type:uint256, order:2, indexed:false, value:150150000000000000, valueString:150150000000000000}], valueString:[{name:assetType, type:tuple, order:1, indexed:false, value:[{name:assetClass, type:bytes4, order:1, indexed:false, value:iuhdhA==, valueString:System.Byte[]}, {name:data, type:bytes, order:2, indexed:false, value:0x000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2, valueString:0x000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2}], valueString:[{name:assetClass, type:bytes4, order:1, indexed:false, value:iuhdhA==, valueString:System.Byte[]}, {name:data, type:bytes, order:2, indexed:false, value:0x000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2, valueString:0x000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2}]}, {name:value, type:uint256, order:2, indexed:false, value:150150000000000000, valueString:150150000000000000}]}, {name:taker, type:address, order:3, indexed:false, value:0x0000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000}, {name:takeAsset, type:tuple, order:4, indexed:false, value:[{name:assetType, type:tuple, order:1, indexed:false, value:[{name:assetClass, type:bytes4, order:1, indexed:false, value:9jwoJQ==, valueString:System.Byte[]}, {name:data, type:bytes, order:2, indexed:false, value:0x0000000000000000000000007E5CA13295B580128D9E9BD46756F2368C9AA263, valueString:0x0000000000000000000000007E5CA13295B580128D9E9BD46756F2368C9AA263}], valueString:[{name:assetClass, type:bytes4, order:1, indexed:false, value:9jwoJQ==, valueString:System.Byte[]}, {name:data, type:bytes, order:2, indexed:false, value:0x0000000000000000000000007E5CA13295B580128D9E9BD46756F2368C9AA263, valueString:0x0000000000000000000000007E5CA13295B580128D9E9BD46756F2368C9AA263}]}, {name:value, type:uint256, order:2, indexed:false, value:1, valueString:1}], valueString:[{name:assetType, type:tuple, order:1, indexed:false, value:[{name:assetClass, type:bytes4, order:1, indexed:false, value:9jwoJQ==, valueString:System.Byte[]}, {name:data, type:bytes, order:2, indexed:false, value:0x0000000000000000000000007E5CA13295B580128D9E9BD46756F2368C9AA263, valueString:0x0000000000000000000000007E5CA13295B580128D9E9BD46756F2368C9AA263}], valueString:[{name:assetClass, type:bytes4, order:1, indexed:false, value:9jwoJQ==, valueString:System.Byte[]}, {name:data, type:bytes, order:2, indexed:false, value:0x0000000000000000000000007E5CA13295B580128D9E9BD46756F2368C9AA263, valueString:0x0000000000000000000000007E5CA13295B580128D9E9BD46756F2368C9AA263}]}, {name:value, type:uint256, order:2, indexed:false, value:1, valueString:1}]}, {name:salt, type:uint256, order:5, indexed:false, value:69849831966832858047106829227108587196032966909799638989244300058720473894244, valueString:69849831966832858047106829227108587196032966909799638989244300058720473894244}, {name:start, type:uint256, order:6, indexed:false, value:0, valueString:0}, {name:end, type:uint256, order:7, indexed:false, value:1750970684, valueString:1750970684}, {name:dataType, type:bytes4, order:8, indexed:false, value:St5Uyg==, valueString:System.Byte[]}, {name:data, type:bytes, order:9, indexed:false, value:0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000001CF0DF2A5A20CD61D68D4489EEBBF85B8D39E18A00000000000000000000000000000000000000000000000000000000000001DB, valueString:0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000001CF0DF2A5A20CD61D68D4489EEBBF85B8D39E18A00000000000000000000000000000000000000000000000000000000000001DB}], signatureLeft=0xA5743D285618794822C70ED182CC9B69A6FFBD2A86A7842813B73D02F7E8F24A382D46F6C0F7B3771E7B98D43A39097E64976B8B21DE979FB8EDEFE81724D0661C, orderRight=[{name:maker, type:address, order:1, indexed:false, value:0x020C811f28441166127C67517101BD9ad15ef433, valueString:0x020C811f28441166127C67517101BD9ad15ef433}, {name:makeAsset, type:tuple, order:2, indexed:false, value:[{name:assetType, type:tuple, order:1, indexed:false, value:[{name:assetClass, type:bytes4, order:1, indexed:false, value:c60hRg==, valueString:System.Byte[]}, {name:data, type:bytes, order:2, indexed:false, value:0x0000000000000000000000007E5CA13295B580128D9E9BD46756F2368C9AA263000000000000000000000000000000000000000000000000000000000000004F, valueString:0x0000000000000000000000007E5CA13295B580128D9E9BD46756F2368C9AA263000000000000000000000000000000000000000000000000000000000000004F}], valueString:[{name:assetClass, type:bytes4, order:1, indexed:false, value:c60hRg==, valueString:System.Byte[]}, {name:data, type:bytes, order:2, indexed:false, value:0x0000000000000000000000007E5CA13295B580128D9E9BD46756F2368C9AA263000000000000000000000000000000000000000000000000000000000000004F, valueString:0x0000000000000000000000007E5CA13295B580128D9E9BD46756F2368C9AA263000000000000000000000000000000000000000000000000000000000000004F}]}, {name:value, type:uint256, order:2, indexed:false, value:1, valueString:1}], valueString:[{name:assetType, type:tuple, order:1, indexed:false, value:[{name:assetClass, type:bytes4, order:1, indexed:false, value:c60hRg==, valueString:System.Byte[]}, {name:data, type:bytes, order:2, indexed:false, value:0x0000000000000000000000007E5CA13295B580128D9E9BD46756F2368C9AA263000000000000000000000000000000000000000000000000000000000000004F, valueString:0x0000000000000000000000007E5CA13295B580128D9E9BD46756F2368C9AA263000000000000000000000000000000000000000000000000000000000000004F}], valueString:[{name:assetClass, type:bytes4, order:1, indexed:false, value:c60hRg==, valueString:System.Byte[]}, {name:data, type:bytes, order:2, indexed:false, value:0x0000000000000000000000007E5CA13295B580128D9E9BD46756F2368C9AA263000000000000000000000000000000000000000000000000000000000000004F, valueString:0x0000000000000000000000007E5CA13295B580128D9E9BD46756F2368C9AA263000000000000000000000000000000000000000000000000000000000000004F}]}, {name:value, type:uint256, order:2, indexed:false, value:1, valueString:1}]}, {name:taker, type:address, order:3, indexed:false, value:0x60f759C538a16DE173a0bA48533c952Ed541A7FE, valueString:0x60f759C538a16DE173a0bA48533c952Ed541A7FE}, {name:takeAsset, type:tuple, order:4, indexed:false, value:[{name:assetType, type:tuple, order:1, indexed:false, value:[{name:assetClass, type:bytes4, order:1, indexed:false, value:iuhdhA==, valueString:System.Byte[]}, {name:data, type:bytes, order:2, indexed:false, value:0x000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2, valueString:0x000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2}], valueString:[{name:assetClass, type:bytes4, order:1, indexed:false, value:iuhdhA==, valueString:System.Byte[]}, {name:data, type:bytes, order:2, indexed:false, value:0x000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2, valueString:0x000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2}]}, {name:value, type:uint256, order:2, indexed:false, value:150150000000000000, valueString:150150000000000000}], valueString:[{name:assetType, type:tuple, order:1, indexed:false, value:[{name:assetClass, type:bytes4, order:1, indexed:false, value:iuhdhA==, valueString:System.Byte[]}, {name:data, type:bytes, order:2, indexed:false, value:0x000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2, valueString:0x000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2}], valueString:[{name:assetClass, type:bytes4, order:1, indexed:false, value:iuhdhA==, valueString:System.Byte[]}, {name:data, type:bytes, order:2, indexed:false, value:0x000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2, valueString:0x000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2}]}, {name:value, type:uint256, order:2, indexed:false, value:150150000000000000, valueString:150150000000000000}]}, {name:salt, type:uint256, order:5, indexed:false, value:0, valueString:0}, {name:start, type:uint256, order:6, indexed:false, value:0, valueString:0}, {name:end, type:uint256, order:7, indexed:false, value:1750970684, valueString:1750970684}, {name:dataType, type:bytes4, order:8, indexed:false, value:St5Uyg==, valueString:System.Byte[]}, {name:data, type:bytes, order:9, indexed:false, value:0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000001CF0DF2A5A20CD61D68D4489EEBBF85B8D39E18A00000000000000000000000000000000000000000000000000000000000001DB, valueString:0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000001CF0DF2A5A20CD61D68D4489EEBBF85B8D39E18A00000000000000000000000000000000000000000000000000000000000001DB}], signatureRight=0x )
    • Null: 0x000...001.ddbbd7c3( )
    • Rarible: Asset Matcher Collection.6d3f7cb0( )
    • ERC20TransferProxy.erc20safeTransferFrom( token=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, from=0x60f759C538a16DE173a0bA48533c952Ed541A7FE, to=0x7e9c956e3EFA81Ace71905Ff0dAEf1A71f42CBC5, value=750750000000000 )
      • WETH9.transferFrom( src=0x60f759C538a16DE173a0bA48533c952Ed541A7FE, dst=0x7e9c956e3EFA81Ace71905Ff0dAEf1A71f42CBC5, wad=750750000000000 ) => ( True )
      • TransparentUpgradeableProxy.9ca7dc7a( )
        • RoyaltiesRegistry.getRoyalties( token=0x7E5Ca13295B580128d9e9BD46756f2368C9Aa263, tokenId=79 ) => ( )
          • DropERC721.royaltyInfo( tokenId=79, salePrice=1000000 ) => ( receiver=0x4bBf570791B989152CFa7EC4dAEF5d14C494C806, royaltyAmount=50000 )
            • DropERC721.royaltyInfo( tokenId=79, salePrice=1000000 ) => ( receiver=0x4bBf570791B989152CFa7EC4dAEF5d14C494C806, royaltyAmount=50000 )
            • ERC20TransferProxy.erc20safeTransferFrom( token=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, from=0x60f759C538a16DE173a0bA48533c952Ed541A7FE, to=0x4bBf570791B989152CFa7EC4dAEF5d14C494C806, value=7507500000000000 )
              • WETH9.transferFrom( src=0x60f759C538a16DE173a0bA48533c952Ed541A7FE, dst=0x4bBf570791B989152CFa7EC4dAEF5d14C494C806, wad=7507500000000000 ) => ( True )
              • ERC20TransferProxy.erc20safeTransferFrom( token=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, from=0x60f759C538a16DE173a0bA48533c952Ed541A7FE, to=0x1cf0dF2A5A20Cd61d68d4489eEBbf85b8d39e18a, value=14264250000000000 )
                • WETH9.transferFrom( src=0x60f759C538a16DE173a0bA48533c952Ed541A7FE, dst=0x1cf0dF2A5A20Cd61d68d4489eEBbf85b8d39e18a, wad=14264250000000000 ) => ( True )
                • ERC20TransferProxy.erc20safeTransferFrom( token=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, from=0x60f759C538a16DE173a0bA48533c952Ed541A7FE, to=0x020C811f28441166127C67517101BD9ad15ef433, value=135135000000000000 )
                  • WETH9.transferFrom( src=0x60f759C538a16DE173a0bA48533c952Ed541A7FE, dst=0x020C811f28441166127C67517101BD9ad15ef433, wad=135135000000000000 ) => ( True )
                  • TransferProxy.erc721safeTransferFrom( token=0x7E5Ca13295B580128d9e9BD46756f2368C9Aa263, from=0x020C811f28441166127C67517101BD9ad15ef433, to=0x60f759C538a16DE173a0bA48533c952Ed541A7FE, tokenId=79 )
                    • DropERC721.safeTransferFrom( from=0x020C811f28441166127C67517101BD9ad15ef433, to=0x60f759C538a16DE173a0bA48533c952Ed541A7FE, tokenId=79 )
                      • DropERC721.safeTransferFrom( from=0x020C811f28441166127C67517101BD9ad15ef433, to=0x60f759C538a16DE173a0bA48533c952Ed541A7FE, tokenId=79 )
                        File 1 of 11: TransparentUpgradeableProxy
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
                        import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
                        import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
                        // Kept for backwards compatibility with older versions of Hardhat and Truffle plugins.
                        contract AdminUpgradeabilityProxy is TransparentUpgradeableProxy {
                            constructor(address logic, address admin, bytes memory data) payable TransparentUpgradeableProxy(logic, admin, data) {}
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        import "../Proxy.sol";
                        import "./ERC1967Upgrade.sol";
                        /**
                         * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
                         * implementation address that can be changed. This address is stored in storage in the location specified by
                         * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
                         * implementation behind the proxy.
                         */
                        contract ERC1967Proxy is Proxy, ERC1967Upgrade {
                            /**
                             * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
                             *
                             * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
                             * function call, and allows initializating the storage of the proxy like a Solidity constructor.
                             */
                            constructor(address _logic, bytes memory _data) payable {
                                assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                                _upgradeToAndCall(_logic, _data, false);
                            }
                            /**
                             * @dev Returns the current implementation address.
                             */
                            function _implementation() internal view virtual override returns (address impl) {
                                return ERC1967Upgrade._getImplementation();
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        import "../ERC1967/ERC1967Proxy.sol";
                        /**
                         * @dev This contract implements a proxy that is upgradeable by an admin.
                         *
                         * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
                         * clashing], which can potentially be used in an attack, this contract uses the
                         * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
                         * things that go hand in hand:
                         *
                         * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
                         * that call matches one of the admin functions exposed by the proxy itself.
                         * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
                         * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
                         * "admin cannot fallback to proxy target".
                         *
                         * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
                         * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
                         * to sudden errors when trying to call a function from the proxy implementation.
                         *
                         * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
                         * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
                         */
                        contract TransparentUpgradeableProxy is ERC1967Proxy {
                            /**
                             * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                             * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
                             */
                            constructor(address _logic, address admin_, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
                                assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                                _changeAdmin(admin_);
                            }
                            /**
                             * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                             */
                            modifier ifAdmin() {
                                if (msg.sender == _getAdmin()) {
                                    _;
                                } else {
                                    _fallback();
                                }
                            }
                            /**
                             * @dev Returns the current admin.
                             *
                             * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
                             *
                             * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                             * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                             * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                             */
                            function admin() external ifAdmin returns (address admin_) {
                                admin_ = _getAdmin();
                            }
                            /**
                             * @dev Returns the current implementation.
                             *
                             * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
                             *
                             * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                             * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                             * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                             */
                            function implementation() external ifAdmin returns (address implementation_) {
                                implementation_ = _implementation();
                            }
                            /**
                             * @dev Changes the admin of the proxy.
                             *
                             * Emits an {AdminChanged} event.
                             *
                             * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
                             */
                            function changeAdmin(address newAdmin) external virtual ifAdmin {
                                _changeAdmin(newAdmin);
                            }
                            /**
                             * @dev Upgrade the implementation of the proxy.
                             *
                             * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
                             */
                            function upgradeTo(address newImplementation) external ifAdmin {
                                _upgradeToAndCall(newImplementation, bytes(""), false);
                            }
                            /**
                             * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                             * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                             * proxied contract.
                             *
                             * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
                             */
                            function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
                                _upgradeToAndCall(newImplementation, data, true);
                            }
                            /**
                             * @dev Returns the current admin.
                             */
                            function _admin() internal view virtual returns (address) {
                                return _getAdmin();
                            }
                            /**
                             * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
                             */
                            function _beforeFallback() internal virtual override {
                                require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                                super._beforeFallback();
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        import "./TransparentUpgradeableProxy.sol";
                        import "../../access/Ownable.sol";
                        /**
                         * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
                         * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
                         */
                        contract ProxyAdmin is Ownable {
                            /**
                             * @dev Returns the current implementation of `proxy`.
                             *
                             * Requirements:
                             *
                             * - This contract must be the admin of `proxy`.
                             */
                            function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
                                // We need to manually run the static call since the getter cannot be flagged as view
                                // bytes4(keccak256("implementation()")) == 0x5c60da1b
                                (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
                                require(success);
                                return abi.decode(returndata, (address));
                            }
                            /**
                             * @dev Returns the current admin of `proxy`.
                             *
                             * Requirements:
                             *
                             * - This contract must be the admin of `proxy`.
                             */
                            function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
                                // We need to manually run the static call since the getter cannot be flagged as view
                                // bytes4(keccak256("admin()")) == 0xf851a440
                                (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
                                require(success);
                                return abi.decode(returndata, (address));
                            }
                            /**
                             * @dev Changes the admin of `proxy` to `newAdmin`.
                             *
                             * Requirements:
                             *
                             * - This contract must be the current admin of `proxy`.
                             */
                            function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {
                                proxy.changeAdmin(newAdmin);
                            }
                            /**
                             * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
                             *
                             * Requirements:
                             *
                             * - This contract must be the admin of `proxy`.
                             */
                            function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {
                                proxy.upgradeTo(implementation);
                            }
                            /**
                             * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
                             * {TransparentUpgradeableProxy-upgradeToAndCall}.
                             *
                             * Requirements:
                             *
                             * - This contract must be the admin of `proxy`.
                             */
                            function upgradeAndCall(TransparentUpgradeableProxy proxy, address implementation, bytes memory data) public payable virtual onlyOwner {
                                proxy.upgradeToAndCall{value: msg.value}(implementation, data);
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        /**
                         * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
                         * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
                         * be specified by overriding the virtual {_implementation} function.
                         *
                         * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
                         * different contract through the {_delegate} function.
                         *
                         * The success and return data of the delegated call will be returned back to the caller of the proxy.
                         */
                        abstract contract Proxy {
                            /**
                             * @dev Delegates the current call to `implementation`.
                             *
                             * This function does not return to its internall call site, it will return directly to the external caller.
                             */
                            function _delegate(address implementation) internal virtual {
                                // solhint-disable-next-line no-inline-assembly
                                assembly {
                                    // Copy msg.data. We take full control of memory in this inline assembly
                                    // block because it will not return to Solidity code. We overwrite the
                                    // Solidity scratch pad at memory position 0.
                                    calldatacopy(0, 0, calldatasize())
                                    // Call the implementation.
                                    // out and outsize are 0 because we don't know the size yet.
                                    let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                                    // Copy the returned data.
                                    returndatacopy(0, 0, returndatasize())
                                    switch result
                                    // delegatecall returns 0 on error.
                                    case 0 { revert(0, returndatasize()) }
                                    default { return(0, returndatasize()) }
                                }
                            }
                            /**
                             * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
                             * and {_fallback} should delegate.
                             */
                            function _implementation() internal view virtual returns (address);
                            /**
                             * @dev Delegates the current call to the address returned by `_implementation()`.
                             *
                             * This function does not return to its internall call site, it will return directly to the external caller.
                             */
                            function _fallback() internal virtual {
                                _beforeFallback();
                                _delegate(_implementation());
                            }
                            /**
                             * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
                             * function in the contract matches the call data.
                             */
                            fallback () external payable virtual {
                                _fallback();
                            }
                            /**
                             * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                             * is empty.
                             */
                            receive () external payable virtual {
                                _fallback();
                            }
                            /**
                             * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
                             * call, or as part of the Solidity `fallback` or `receive` functions.
                             *
                             * If overriden should call `super._beforeFallback()`.
                             */
                            function _beforeFallback() internal virtual {
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.2;
                        import "../beacon/IBeacon.sol";
                        import "../../utils/Address.sol";
                        import "../../utils/StorageSlot.sol";
                        /**
                         * @dev This abstract contract provides getters and event emitting update functions for
                         * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
                         *
                         * _Available since v4.1._
                         *
                         * @custom:oz-upgrades-unsafe-allow delegatecall
                         */
                        abstract contract ERC1967Upgrade {
                            // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                            bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                            /**
                             * @dev Storage slot with the address of the current implementation.
                             * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                             * validated in the constructor.
                             */
                            bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                            /**
                             * @dev Emitted when the implementation is upgraded.
                             */
                            event Upgraded(address indexed implementation);
                            /**
                             * @dev Returns the current implementation address.
                             */
                            function _getImplementation() internal view returns (address) {
                                return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                            }
                            /**
                             * @dev Stores a new address in the EIP1967 implementation slot.
                             */
                            function _setImplementation(address newImplementation) private {
                                require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                                StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                            }
                            /**
                             * @dev Perform implementation upgrade
                             *
                             * Emits an {Upgraded} event.
                             */
                            function _upgradeTo(address newImplementation) internal {
                                _setImplementation(newImplementation);
                                emit Upgraded(newImplementation);
                            }
                            /**
                             * @dev Perform implementation upgrade with additional setup call.
                             *
                             * Emits an {Upgraded} event.
                             */
                            function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
                                _setImplementation(newImplementation);
                                emit Upgraded(newImplementation);
                                if (data.length > 0 || forceCall) {
                                    Address.functionDelegateCall(newImplementation, data);
                                }
                            }
                            /**
                             * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                             *
                             * Emits an {Upgraded} event.
                             */
                            function _upgradeToAndCallSecure(address newImplementation, bytes memory data, bool forceCall) internal {
                                address oldImplementation = _getImplementation();
                                // Initial upgrade and setup call
                                _setImplementation(newImplementation);
                                if (data.length > 0 || forceCall) {
                                    Address.functionDelegateCall(newImplementation, data);
                                }
                                // Perform rollback test if not already in progress
                                StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);
                                if (!rollbackTesting.value) {
                                    // Trigger rollback using upgradeTo from the new implementation
                                    rollbackTesting.value = true;
                                    Address.functionDelegateCall(
                                        newImplementation,
                                        abi.encodeWithSignature(
                                            "upgradeTo(address)",
                                            oldImplementation
                                        )
                                    );
                                    rollbackTesting.value = false;
                                    // Check rollback was effective
                                    require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
                                    // Finally reset to the new implementation and log the upgrade
                                    _setImplementation(newImplementation);
                                    emit Upgraded(newImplementation);
                                }
                            }
                            /**
                             * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                             * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                             *
                             * Emits a {BeaconUpgraded} event.
                             */
                            function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
                                _setBeacon(newBeacon);
                                emit BeaconUpgraded(newBeacon);
                                if (data.length > 0 || forceCall) {
                                    Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                                }
                            }
                            /**
                             * @dev Storage slot with the admin of the contract.
                             * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                             * validated in the constructor.
                             */
                            bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                            /**
                             * @dev Emitted when the admin account has changed.
                             */
                            event AdminChanged(address previousAdmin, address newAdmin);
                            /**
                             * @dev Returns the current admin.
                             */
                            function _getAdmin() internal view returns (address) {
                                return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
                            }
                            /**
                             * @dev Stores a new address in the EIP1967 admin slot.
                             */
                            function _setAdmin(address newAdmin) private {
                                require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                                StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                            }
                            /**
                             * @dev Changes the admin of the proxy.
                             *
                             * Emits an {AdminChanged} event.
                             */
                            function _changeAdmin(address newAdmin) internal {
                                emit AdminChanged(_getAdmin(), newAdmin);
                                _setAdmin(newAdmin);
                            }
                            /**
                             * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                             * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                             */
                            bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                            /**
                             * @dev Emitted when the beacon is upgraded.
                             */
                            event BeaconUpgraded(address indexed beacon);
                            /**
                             * @dev Returns the current beacon.
                             */
                            function _getBeacon() internal view returns (address) {
                                return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
                            }
                            /**
                             * @dev Stores a new beacon in the EIP1967 beacon slot.
                             */
                            function _setBeacon(address newBeacon) private {
                                require(
                                    Address.isContract(newBeacon),
                                    "ERC1967: new beacon is not a contract"
                                );
                                require(
                                    Address.isContract(IBeacon(newBeacon).implementation()),
                                    "ERC1967: beacon implementation is not a contract"
                                );
                                StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        /**
                         * @dev This is the interface that {BeaconProxy} expects of its beacon.
                         */
                        interface IBeacon {
                            /**
                             * @dev Must return an address that can be used as a delegate call target.
                             *
                             * {BeaconProxy} will check that this address is a contract.
                             */
                            function implementation() external view returns (address);
                        }
                        // SPDX-License-Identifier: MIT
                        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;
                                // solhint-disable-next-line no-inline-assembly
                                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");
                                // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                                (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");
                                // solhint-disable-next-line avoid-low-level-calls
                                (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");
                                // solhint-disable-next-line avoid-low-level-calls
                                (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");
                                // solhint-disable-next-line avoid-low-level-calls
                                (bool success, bytes memory returndata) = target.delegatecall(data);
                                return _verifyCallResult(success, returndata, errorMessage);
                            }
                            function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private 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
                                        // solhint-disable-next-line no-inline-assembly
                                        assembly {
                                            let returndata_size := mload(returndata)
                                            revert(add(32, returndata), returndata_size)
                                        }
                                    } else {
                                        revert(errorMessage);
                                    }
                                }
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        /**
                         * @dev Library for reading and writing primitive types to specific storage slots.
                         *
                         * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
                         * This library helps with reading and writing to such slots without the need for inline assembly.
                         *
                         * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
                         *
                         * Example usage to set ERC1967 implementation slot:
                         * ```
                         * contract ERC1967 {
                         *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                         *
                         *     function _getImplementation() internal view returns (address) {
                         *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                         *     }
                         *
                         *     function _setImplementation(address newImplementation) internal {
                         *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                         *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                         *     }
                         * }
                         * ```
                         *
                         * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
                         */
                        library StorageSlot {
                            struct AddressSlot {
                                address value;
                            }
                            struct BooleanSlot {
                                bool value;
                            }
                            struct Bytes32Slot {
                                bytes32 value;
                            }
                            struct Uint256Slot {
                                uint256 value;
                            }
                            /**
                             * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                             */
                            function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                                assembly {
                                    r.slot := slot
                                }
                            }
                            /**
                             * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                             */
                            function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                                assembly {
                                    r.slot := slot
                                }
                            }
                            /**
                             * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                             */
                            function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                                assembly {
                                    r.slot := slot
                                }
                            }
                            /**
                             * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                             */
                            function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                                assembly {
                                    r.slot := slot
                                }
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        import "../utils/Context.sol";
                        /**
                         * @dev Contract module which provides a basic access control mechanism, where
                         * there is an account (an owner) that can be granted exclusive access to
                         * specific functions.
                         *
                         * By default, the owner account will be the one that deploys the contract. This
                         * can later be changed with {transferOwnership}.
                         *
                         * This module is used through inheritance. It will make available the modifier
                         * `onlyOwner`, which can be applied to your functions to restrict their use to
                         * the owner.
                         */
                        abstract contract Ownable is Context {
                            address private _owner;
                            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                            /**
                             * @dev Initializes the contract setting the deployer as the initial owner.
                             */
                            constructor () {
                                address msgSender = _msgSender();
                                _owner = msgSender;
                                emit OwnershipTransferred(address(0), msgSender);
                            }
                            /**
                             * @dev Returns the address of the current owner.
                             */
                            function owner() public view virtual returns (address) {
                                return _owner;
                            }
                            /**
                             * @dev Throws if called by any account other than the owner.
                             */
                            modifier onlyOwner() {
                                require(owner() == _msgSender(), "Ownable: caller is not the owner");
                                _;
                            }
                            /**
                             * @dev Leaves the contract without owner. It will not be possible to call
                             * `onlyOwner` functions anymore. Can only be called by the current owner.
                             *
                             * NOTE: Renouncing ownership will leave the contract without an owner,
                             * thereby removing any functionality that is only available to the owner.
                             */
                            function renounceOwnership() public virtual onlyOwner {
                                emit OwnershipTransferred(_owner, address(0));
                                _owner = address(0);
                            }
                            /**
                             * @dev Transfers ownership of the contract to a new account (`newOwner`).
                             * Can only be called by the current owner.
                             */
                            function transferOwnership(address newOwner) public virtual onlyOwner {
                                require(newOwner != address(0), "Ownable: new owner is the zero address");
                                emit OwnershipTransferred(_owner, newOwner);
                                _owner = newOwner;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        /*
                         * @dev Provides information about the current execution context, including the
                         * sender of the transaction and its data. While these are generally available
                         * via msg.sender and msg.data, they should not be accessed in such a direct
                         * manner, since when dealing with meta-transactions the account sending and
                         * paying for execution may not be the actual sender (as far as an application
                         * is concerned).
                         *
                         * This contract is only required for intermediate, library-like contracts.
                         */
                        abstract contract Context {
                            function _msgSender() internal view virtual returns (address) {
                                return msg.sender;
                            }
                            function _msgData() internal view virtual returns (bytes calldata) {
                                this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                                return msg.data;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        import "../ERC1967/ERC1967Upgrade.sol";
                        /**
                         * @dev Base contract for building openzeppelin-upgrades compatible implementations for the {ERC1967Proxy}. It includes
                         * publicly available upgrade functions that are called by the plugin and by the secure upgrade mechanism to verify
                         * continuation of the upgradability.
                         *
                         * The {_authorizeUpgrade} function MUST be overridden to include access restriction to the upgrade mechanism.
                         *
                         * _Available since v4.1._
                         */
                        abstract contract UUPSUpgradeable is ERC1967Upgrade {
                            function upgradeTo(address newImplementation) external virtual {
                                _authorizeUpgrade(newImplementation);
                                _upgradeToAndCallSecure(newImplementation, bytes(""), false);
                            }
                            function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual {
                                _authorizeUpgrade(newImplementation);
                                _upgradeToAndCallSecure(newImplementation, data, true);
                            }
                            function _authorizeUpgrade(address newImplementation) internal virtual;
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.2;
                        import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
                        abstract contract Proxiable is UUPSUpgradeable {
                            function _authorizeUpgrade(address newImplementation) internal override {
                                _beforeUpgrade(newImplementation);
                            }
                            function _beforeUpgrade(address newImplementation) internal virtual;
                        }
                        contract ChildOfProxiable is Proxiable {
                            function _beforeUpgrade(address newImplementation) internal virtual override {}
                        }
                        

                        File 2 of 11: TransparentUpgradeableProxy
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
                        import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
                        import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
                        // Kept for backwards compatibility with older versions of Hardhat and Truffle plugins.
                        contract AdminUpgradeabilityProxy is TransparentUpgradeableProxy {
                            constructor(address logic, address admin, bytes memory data) payable TransparentUpgradeableProxy(logic, admin, data) {}
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        import "../Proxy.sol";
                        import "./ERC1967Upgrade.sol";
                        /**
                         * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
                         * implementation address that can be changed. This address is stored in storage in the location specified by
                         * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
                         * implementation behind the proxy.
                         */
                        contract ERC1967Proxy is Proxy, ERC1967Upgrade {
                            /**
                             * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
                             *
                             * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
                             * function call, and allows initializating the storage of the proxy like a Solidity constructor.
                             */
                            constructor(address _logic, bytes memory _data) payable {
                                assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                                _upgradeToAndCall(_logic, _data, false);
                            }
                            /**
                             * @dev Returns the current implementation address.
                             */
                            function _implementation() internal view virtual override returns (address impl) {
                                return ERC1967Upgrade._getImplementation();
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        import "../ERC1967/ERC1967Proxy.sol";
                        /**
                         * @dev This contract implements a proxy that is upgradeable by an admin.
                         *
                         * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
                         * clashing], which can potentially be used in an attack, this contract uses the
                         * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
                         * things that go hand in hand:
                         *
                         * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
                         * that call matches one of the admin functions exposed by the proxy itself.
                         * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
                         * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
                         * "admin cannot fallback to proxy target".
                         *
                         * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
                         * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
                         * to sudden errors when trying to call a function from the proxy implementation.
                         *
                         * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
                         * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
                         */
                        contract TransparentUpgradeableProxy is ERC1967Proxy {
                            /**
                             * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                             * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
                             */
                            constructor(address _logic, address admin_, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
                                assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                                _changeAdmin(admin_);
                            }
                            /**
                             * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                             */
                            modifier ifAdmin() {
                                if (msg.sender == _getAdmin()) {
                                    _;
                                } else {
                                    _fallback();
                                }
                            }
                            /**
                             * @dev Returns the current admin.
                             *
                             * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
                             *
                             * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                             * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                             * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                             */
                            function admin() external ifAdmin returns (address admin_) {
                                admin_ = _getAdmin();
                            }
                            /**
                             * @dev Returns the current implementation.
                             *
                             * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
                             *
                             * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                             * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                             * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                             */
                            function implementation() external ifAdmin returns (address implementation_) {
                                implementation_ = _implementation();
                            }
                            /**
                             * @dev Changes the admin of the proxy.
                             *
                             * Emits an {AdminChanged} event.
                             *
                             * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
                             */
                            function changeAdmin(address newAdmin) external virtual ifAdmin {
                                _changeAdmin(newAdmin);
                            }
                            /**
                             * @dev Upgrade the implementation of the proxy.
                             *
                             * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
                             */
                            function upgradeTo(address newImplementation) external ifAdmin {
                                _upgradeToAndCall(newImplementation, bytes(""), false);
                            }
                            /**
                             * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                             * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                             * proxied contract.
                             *
                             * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
                             */
                            function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
                                _upgradeToAndCall(newImplementation, data, true);
                            }
                            /**
                             * @dev Returns the current admin.
                             */
                            function _admin() internal view virtual returns (address) {
                                return _getAdmin();
                            }
                            /**
                             * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
                             */
                            function _beforeFallback() internal virtual override {
                                require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                                super._beforeFallback();
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        import "./TransparentUpgradeableProxy.sol";
                        import "../../access/Ownable.sol";
                        /**
                         * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
                         * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
                         */
                        contract ProxyAdmin is Ownable {
                            /**
                             * @dev Returns the current implementation of `proxy`.
                             *
                             * Requirements:
                             *
                             * - This contract must be the admin of `proxy`.
                             */
                            function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
                                // We need to manually run the static call since the getter cannot be flagged as view
                                // bytes4(keccak256("implementation()")) == 0x5c60da1b
                                (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
                                require(success);
                                return abi.decode(returndata, (address));
                            }
                            /**
                             * @dev Returns the current admin of `proxy`.
                             *
                             * Requirements:
                             *
                             * - This contract must be the admin of `proxy`.
                             */
                            function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
                                // We need to manually run the static call since the getter cannot be flagged as view
                                // bytes4(keccak256("admin()")) == 0xf851a440
                                (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
                                require(success);
                                return abi.decode(returndata, (address));
                            }
                            /**
                             * @dev Changes the admin of `proxy` to `newAdmin`.
                             *
                             * Requirements:
                             *
                             * - This contract must be the current admin of `proxy`.
                             */
                            function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {
                                proxy.changeAdmin(newAdmin);
                            }
                            /**
                             * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
                             *
                             * Requirements:
                             *
                             * - This contract must be the admin of `proxy`.
                             */
                            function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {
                                proxy.upgradeTo(implementation);
                            }
                            /**
                             * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
                             * {TransparentUpgradeableProxy-upgradeToAndCall}.
                             *
                             * Requirements:
                             *
                             * - This contract must be the admin of `proxy`.
                             */
                            function upgradeAndCall(TransparentUpgradeableProxy proxy, address implementation, bytes memory data) public payable virtual onlyOwner {
                                proxy.upgradeToAndCall{value: msg.value}(implementation, data);
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        /**
                         * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
                         * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
                         * be specified by overriding the virtual {_implementation} function.
                         *
                         * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
                         * different contract through the {_delegate} function.
                         *
                         * The success and return data of the delegated call will be returned back to the caller of the proxy.
                         */
                        abstract contract Proxy {
                            /**
                             * @dev Delegates the current call to `implementation`.
                             *
                             * This function does not return to its internall call site, it will return directly to the external caller.
                             */
                            function _delegate(address implementation) internal virtual {
                                // solhint-disable-next-line no-inline-assembly
                                assembly {
                                    // Copy msg.data. We take full control of memory in this inline assembly
                                    // block because it will not return to Solidity code. We overwrite the
                                    // Solidity scratch pad at memory position 0.
                                    calldatacopy(0, 0, calldatasize())
                                    // Call the implementation.
                                    // out and outsize are 0 because we don't know the size yet.
                                    let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                                    // Copy the returned data.
                                    returndatacopy(0, 0, returndatasize())
                                    switch result
                                    // delegatecall returns 0 on error.
                                    case 0 { revert(0, returndatasize()) }
                                    default { return(0, returndatasize()) }
                                }
                            }
                            /**
                             * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
                             * and {_fallback} should delegate.
                             */
                            function _implementation() internal view virtual returns (address);
                            /**
                             * @dev Delegates the current call to the address returned by `_implementation()`.
                             *
                             * This function does not return to its internall call site, it will return directly to the external caller.
                             */
                            function _fallback() internal virtual {
                                _beforeFallback();
                                _delegate(_implementation());
                            }
                            /**
                             * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
                             * function in the contract matches the call data.
                             */
                            fallback () external payable virtual {
                                _fallback();
                            }
                            /**
                             * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                             * is empty.
                             */
                            receive () external payable virtual {
                                _fallback();
                            }
                            /**
                             * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
                             * call, or as part of the Solidity `fallback` or `receive` functions.
                             *
                             * If overriden should call `super._beforeFallback()`.
                             */
                            function _beforeFallback() internal virtual {
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.2;
                        import "../beacon/IBeacon.sol";
                        import "../../utils/Address.sol";
                        import "../../utils/StorageSlot.sol";
                        /**
                         * @dev This abstract contract provides getters and event emitting update functions for
                         * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
                         *
                         * _Available since v4.1._
                         *
                         * @custom:oz-upgrades-unsafe-allow delegatecall
                         */
                        abstract contract ERC1967Upgrade {
                            // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                            bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                            /**
                             * @dev Storage slot with the address of the current implementation.
                             * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                             * validated in the constructor.
                             */
                            bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                            /**
                             * @dev Emitted when the implementation is upgraded.
                             */
                            event Upgraded(address indexed implementation);
                            /**
                             * @dev Returns the current implementation address.
                             */
                            function _getImplementation() internal view returns (address) {
                                return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                            }
                            /**
                             * @dev Stores a new address in the EIP1967 implementation slot.
                             */
                            function _setImplementation(address newImplementation) private {
                                require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                                StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                            }
                            /**
                             * @dev Perform implementation upgrade
                             *
                             * Emits an {Upgraded} event.
                             */
                            function _upgradeTo(address newImplementation) internal {
                                _setImplementation(newImplementation);
                                emit Upgraded(newImplementation);
                            }
                            /**
                             * @dev Perform implementation upgrade with additional setup call.
                             *
                             * Emits an {Upgraded} event.
                             */
                            function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
                                _setImplementation(newImplementation);
                                emit Upgraded(newImplementation);
                                if (data.length > 0 || forceCall) {
                                    Address.functionDelegateCall(newImplementation, data);
                                }
                            }
                            /**
                             * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                             *
                             * Emits an {Upgraded} event.
                             */
                            function _upgradeToAndCallSecure(address newImplementation, bytes memory data, bool forceCall) internal {
                                address oldImplementation = _getImplementation();
                                // Initial upgrade and setup call
                                _setImplementation(newImplementation);
                                if (data.length > 0 || forceCall) {
                                    Address.functionDelegateCall(newImplementation, data);
                                }
                                // Perform rollback test if not already in progress
                                StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);
                                if (!rollbackTesting.value) {
                                    // Trigger rollback using upgradeTo from the new implementation
                                    rollbackTesting.value = true;
                                    Address.functionDelegateCall(
                                        newImplementation,
                                        abi.encodeWithSignature(
                                            "upgradeTo(address)",
                                            oldImplementation
                                        )
                                    );
                                    rollbackTesting.value = false;
                                    // Check rollback was effective
                                    require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
                                    // Finally reset to the new implementation and log the upgrade
                                    _setImplementation(newImplementation);
                                    emit Upgraded(newImplementation);
                                }
                            }
                            /**
                             * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                             * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                             *
                             * Emits a {BeaconUpgraded} event.
                             */
                            function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
                                _setBeacon(newBeacon);
                                emit BeaconUpgraded(newBeacon);
                                if (data.length > 0 || forceCall) {
                                    Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                                }
                            }
                            /**
                             * @dev Storage slot with the admin of the contract.
                             * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                             * validated in the constructor.
                             */
                            bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                            /**
                             * @dev Emitted when the admin account has changed.
                             */
                            event AdminChanged(address previousAdmin, address newAdmin);
                            /**
                             * @dev Returns the current admin.
                             */
                            function _getAdmin() internal view returns (address) {
                                return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
                            }
                            /**
                             * @dev Stores a new address in the EIP1967 admin slot.
                             */
                            function _setAdmin(address newAdmin) private {
                                require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                                StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                            }
                            /**
                             * @dev Changes the admin of the proxy.
                             *
                             * Emits an {AdminChanged} event.
                             */
                            function _changeAdmin(address newAdmin) internal {
                                emit AdminChanged(_getAdmin(), newAdmin);
                                _setAdmin(newAdmin);
                            }
                            /**
                             * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                             * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                             */
                            bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                            /**
                             * @dev Emitted when the beacon is upgraded.
                             */
                            event BeaconUpgraded(address indexed beacon);
                            /**
                             * @dev Returns the current beacon.
                             */
                            function _getBeacon() internal view returns (address) {
                                return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
                            }
                            /**
                             * @dev Stores a new beacon in the EIP1967 beacon slot.
                             */
                            function _setBeacon(address newBeacon) private {
                                require(
                                    Address.isContract(newBeacon),
                                    "ERC1967: new beacon is not a contract"
                                );
                                require(
                                    Address.isContract(IBeacon(newBeacon).implementation()),
                                    "ERC1967: beacon implementation is not a contract"
                                );
                                StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        /**
                         * @dev This is the interface that {BeaconProxy} expects of its beacon.
                         */
                        interface IBeacon {
                            /**
                             * @dev Must return an address that can be used as a delegate call target.
                             *
                             * {BeaconProxy} will check that this address is a contract.
                             */
                            function implementation() external view returns (address);
                        }
                        // SPDX-License-Identifier: MIT
                        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;
                                // solhint-disable-next-line no-inline-assembly
                                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");
                                // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                                (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");
                                // solhint-disable-next-line avoid-low-level-calls
                                (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");
                                // solhint-disable-next-line avoid-low-level-calls
                                (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");
                                // solhint-disable-next-line avoid-low-level-calls
                                (bool success, bytes memory returndata) = target.delegatecall(data);
                                return _verifyCallResult(success, returndata, errorMessage);
                            }
                            function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private 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
                                        // solhint-disable-next-line no-inline-assembly
                                        assembly {
                                            let returndata_size := mload(returndata)
                                            revert(add(32, returndata), returndata_size)
                                        }
                                    } else {
                                        revert(errorMessage);
                                    }
                                }
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        /**
                         * @dev Library for reading and writing primitive types to specific storage slots.
                         *
                         * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
                         * This library helps with reading and writing to such slots without the need for inline assembly.
                         *
                         * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
                         *
                         * Example usage to set ERC1967 implementation slot:
                         * ```
                         * contract ERC1967 {
                         *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                         *
                         *     function _getImplementation() internal view returns (address) {
                         *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                         *     }
                         *
                         *     function _setImplementation(address newImplementation) internal {
                         *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                         *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                         *     }
                         * }
                         * ```
                         *
                         * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
                         */
                        library StorageSlot {
                            struct AddressSlot {
                                address value;
                            }
                            struct BooleanSlot {
                                bool value;
                            }
                            struct Bytes32Slot {
                                bytes32 value;
                            }
                            struct Uint256Slot {
                                uint256 value;
                            }
                            /**
                             * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                             */
                            function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                                assembly {
                                    r.slot := slot
                                }
                            }
                            /**
                             * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                             */
                            function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                                assembly {
                                    r.slot := slot
                                }
                            }
                            /**
                             * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                             */
                            function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                                assembly {
                                    r.slot := slot
                                }
                            }
                            /**
                             * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                             */
                            function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                                assembly {
                                    r.slot := slot
                                }
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        import "../utils/Context.sol";
                        /**
                         * @dev Contract module which provides a basic access control mechanism, where
                         * there is an account (an owner) that can be granted exclusive access to
                         * specific functions.
                         *
                         * By default, the owner account will be the one that deploys the contract. This
                         * can later be changed with {transferOwnership}.
                         *
                         * This module is used through inheritance. It will make available the modifier
                         * `onlyOwner`, which can be applied to your functions to restrict their use to
                         * the owner.
                         */
                        abstract contract Ownable is Context {
                            address private _owner;
                            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                            /**
                             * @dev Initializes the contract setting the deployer as the initial owner.
                             */
                            constructor () {
                                address msgSender = _msgSender();
                                _owner = msgSender;
                                emit OwnershipTransferred(address(0), msgSender);
                            }
                            /**
                             * @dev Returns the address of the current owner.
                             */
                            function owner() public view virtual returns (address) {
                                return _owner;
                            }
                            /**
                             * @dev Throws if called by any account other than the owner.
                             */
                            modifier onlyOwner() {
                                require(owner() == _msgSender(), "Ownable: caller is not the owner");
                                _;
                            }
                            /**
                             * @dev Leaves the contract without owner. It will not be possible to call
                             * `onlyOwner` functions anymore. Can only be called by the current owner.
                             *
                             * NOTE: Renouncing ownership will leave the contract without an owner,
                             * thereby removing any functionality that is only available to the owner.
                             */
                            function renounceOwnership() public virtual onlyOwner {
                                emit OwnershipTransferred(_owner, address(0));
                                _owner = address(0);
                            }
                            /**
                             * @dev Transfers ownership of the contract to a new account (`newOwner`).
                             * Can only be called by the current owner.
                             */
                            function transferOwnership(address newOwner) public virtual onlyOwner {
                                require(newOwner != address(0), "Ownable: new owner is the zero address");
                                emit OwnershipTransferred(_owner, newOwner);
                                _owner = newOwner;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        /*
                         * @dev Provides information about the current execution context, including the
                         * sender of the transaction and its data. While these are generally available
                         * via msg.sender and msg.data, they should not be accessed in such a direct
                         * manner, since when dealing with meta-transactions the account sending and
                         * paying for execution may not be the actual sender (as far as an application
                         * is concerned).
                         *
                         * This contract is only required for intermediate, library-like contracts.
                         */
                        abstract contract Context {
                            function _msgSender() internal view virtual returns (address) {
                                return msg.sender;
                            }
                            function _msgData() internal view virtual returns (bytes calldata) {
                                this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                                return msg.data;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        import "../ERC1967/ERC1967Upgrade.sol";
                        /**
                         * @dev Base contract for building openzeppelin-upgrades compatible implementations for the {ERC1967Proxy}. It includes
                         * publicly available upgrade functions that are called by the plugin and by the secure upgrade mechanism to verify
                         * continuation of the upgradability.
                         *
                         * The {_authorizeUpgrade} function MUST be overridden to include access restriction to the upgrade mechanism.
                         *
                         * _Available since v4.1._
                         */
                        abstract contract UUPSUpgradeable is ERC1967Upgrade {
                            function upgradeTo(address newImplementation) external virtual {
                                _authorizeUpgrade(newImplementation);
                                _upgradeToAndCallSecure(newImplementation, bytes(""), false);
                            }
                            function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual {
                                _authorizeUpgrade(newImplementation);
                                _upgradeToAndCallSecure(newImplementation, data, true);
                            }
                            function _authorizeUpgrade(address newImplementation) internal virtual;
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.2;
                        import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
                        abstract contract Proxiable is UUPSUpgradeable {
                            function _authorizeUpgrade(address newImplementation) internal override {
                                _beforeUpgrade(newImplementation);
                            }
                            function _beforeUpgrade(address newImplementation) internal virtual;
                        }
                        contract ChildOfProxiable is Proxiable {
                            function _beforeUpgrade(address newImplementation) internal virtual override {}
                        }
                        

                        File 3 of 11: WETH9
                        // Copyright (C) 2015, 2016, 2017 Dapphub
                        
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU General Public License for more details.
                        
                        // You should have received a copy of the GNU General Public License
                        // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        
                        pragma solidity ^0.4.18;
                        
                        contract WETH9 {
                            string public name     = "Wrapped Ether";
                            string public symbol   = "WETH";
                            uint8  public decimals = 18;
                        
                            event  Approval(address indexed src, address indexed guy, uint wad);
                            event  Transfer(address indexed src, address indexed dst, uint wad);
                            event  Deposit(address indexed dst, uint wad);
                            event  Withdrawal(address indexed src, uint wad);
                        
                            mapping (address => uint)                       public  balanceOf;
                            mapping (address => mapping (address => uint))  public  allowance;
                        
                            function() public payable {
                                deposit();
                            }
                            function deposit() public payable {
                                balanceOf[msg.sender] += msg.value;
                                Deposit(msg.sender, msg.value);
                            }
                            function withdraw(uint wad) public {
                                require(balanceOf[msg.sender] >= wad);
                                balanceOf[msg.sender] -= wad;
                                msg.sender.transfer(wad);
                                Withdrawal(msg.sender, wad);
                            }
                        
                            function totalSupply() public view returns (uint) {
                                return this.balance;
                            }
                        
                            function approve(address guy, uint wad) public returns (bool) {
                                allowance[msg.sender][guy] = wad;
                                Approval(msg.sender, guy, wad);
                                return true;
                            }
                        
                            function transfer(address dst, uint wad) public returns (bool) {
                                return transferFrom(msg.sender, dst, wad);
                            }
                        
                            function transferFrom(address src, address dst, uint wad)
                                public
                                returns (bool)
                            {
                                require(balanceOf[src] >= wad);
                        
                                if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
                                    require(allowance[src][msg.sender] >= wad);
                                    allowance[src][msg.sender] -= wad;
                                }
                        
                                balanceOf[src] -= wad;
                                balanceOf[dst] += wad;
                        
                                Transfer(src, dst, wad);
                        
                                return true;
                            }
                        }
                        
                        
                        /*
                                            GNU GENERAL PUBLIC LICENSE
                                               Version 3, 29 June 2007
                        
                         Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
                         Everyone is permitted to copy and distribute verbatim copies
                         of this license document, but changing it is not allowed.
                        
                                                    Preamble
                        
                          The GNU General Public License is a free, copyleft license for
                        software and other kinds of works.
                        
                          The licenses for most software and other practical works are designed
                        to take away your freedom to share and change the works.  By contrast,
                        the GNU General Public License is intended to guarantee your freedom to
                        share and change all versions of a program--to make sure it remains free
                        software for all its users.  We, the Free Software Foundation, use the
                        GNU General Public License for most of our software; it applies also to
                        any other work released this way by its authors.  You can apply it to
                        your programs, too.
                        
                          When we speak of free software, we are referring to freedom, not
                        price.  Our General Public Licenses are designed to make sure that you
                        have the freedom to distribute copies of free software (and charge for
                        them if you wish), that you receive source code or can get it if you
                        want it, that you can change the software or use pieces of it in new
                        free programs, and that you know you can do these things.
                        
                          To protect your rights, we need to prevent others from denying you
                        these rights or asking you to surrender the rights.  Therefore, you have
                        certain responsibilities if you distribute copies of the software, or if
                        you modify it: responsibilities to respect the freedom of others.
                        
                          For example, if you distribute copies of such a program, whether
                        gratis or for a fee, you must pass on to the recipients the same
                        freedoms that you received.  You must make sure that they, too, receive
                        or can get the source code.  And you must show them these terms so they
                        know their rights.
                        
                          Developers that use the GNU GPL protect your rights with two steps:
                        (1) assert copyright on the software, and (2) offer you this License
                        giving you legal permission to copy, distribute and/or modify it.
                        
                          For the developers' and authors' protection, the GPL clearly explains
                        that there is no warranty for this free software.  For both users' and
                        authors' sake, the GPL requires that modified versions be marked as
                        changed, so that their problems will not be attributed erroneously to
                        authors of previous versions.
                        
                          Some devices are designed to deny users access to install or run
                        modified versions of the software inside them, although the manufacturer
                        can do so.  This is fundamentally incompatible with the aim of
                        protecting users' freedom to change the software.  The systematic
                        pattern of such abuse occurs in the area of products for individuals to
                        use, which is precisely where it is most unacceptable.  Therefore, we
                        have designed this version of the GPL to prohibit the practice for those
                        products.  If such problems arise substantially in other domains, we
                        stand ready to extend this provision to those domains in future versions
                        of the GPL, as needed to protect the freedom of users.
                        
                          Finally, every program is threatened constantly by software patents.
                        States should not allow patents to restrict development and use of
                        software on general-purpose computers, but in those that do, we wish to
                        avoid the special danger that patents applied to a free program could
                        make it effectively proprietary.  To prevent this, the GPL assures that
                        patents cannot be used to render the program non-free.
                        
                          The precise terms and conditions for copying, distribution and
                        modification follow.
                        
                                               TERMS AND CONDITIONS
                        
                          0. Definitions.
                        
                          "This License" refers to version 3 of the GNU General Public License.
                        
                          "Copyright" also means copyright-like laws that apply to other kinds of
                        works, such as semiconductor masks.
                        
                          "The Program" refers to any copyrightable work licensed under this
                        License.  Each licensee is addressed as "you".  "Licensees" and
                        "recipients" may be individuals or organizations.
                        
                          To "modify" a work means to copy from or adapt all or part of the work
                        in a fashion requiring copyright permission, other than the making of an
                        exact copy.  The resulting work is called a "modified version" of the
                        earlier work or a work "based on" the earlier work.
                        
                          A "covered work" means either the unmodified Program or a work based
                        on the Program.
                        
                          To "propagate" a work means to do anything with it that, without
                        permission, would make you directly or secondarily liable for
                        infringement under applicable copyright law, except executing it on a
                        computer or modifying a private copy.  Propagation includes copying,
                        distribution (with or without modification), making available to the
                        public, and in some countries other activities as well.
                        
                          To "convey" a work means any kind of propagation that enables other
                        parties to make or receive copies.  Mere interaction with a user through
                        a computer network, with no transfer of a copy, is not conveying.
                        
                          An interactive user interface displays "Appropriate Legal Notices"
                        to the extent that it includes a convenient and prominently visible
                        feature that (1) displays an appropriate copyright notice, and (2)
                        tells the user that there is no warranty for the work (except to the
                        extent that warranties are provided), that licensees may convey the
                        work under this License, and how to view a copy of this License.  If
                        the interface presents a list of user commands or options, such as a
                        menu, a prominent item in the list meets this criterion.
                        
                          1. Source Code.
                        
                          The "source code" for a work means the preferred form of the work
                        for making modifications to it.  "Object code" means any non-source
                        form of a work.
                        
                          A "Standard Interface" means an interface that either is an official
                        standard defined by a recognized standards body, or, in the case of
                        interfaces specified for a particular programming language, one that
                        is widely used among developers working in that language.
                        
                          The "System Libraries" of an executable work include anything, other
                        than the work as a whole, that (a) is included in the normal form of
                        packaging a Major Component, but which is not part of that Major
                        Component, and (b) serves only to enable use of the work with that
                        Major Component, or to implement a Standard Interface for which an
                        implementation is available to the public in source code form.  A
                        "Major Component", in this context, means a major essential component
                        (kernel, window system, and so on) of the specific operating system
                        (if any) on which the executable work runs, or a compiler used to
                        produce the work, or an object code interpreter used to run it.
                        
                          The "Corresponding Source" for a work in object code form means all
                        the source code needed to generate, install, and (for an executable
                        work) run the object code and to modify the work, including scripts to
                        control those activities.  However, it does not include the work's
                        System Libraries, or general-purpose tools or generally available free
                        programs which are used unmodified in performing those activities but
                        which are not part of the work.  For example, Corresponding Source
                        includes interface definition files associated with source files for
                        the work, and the source code for shared libraries and dynamically
                        linked subprograms that the work is specifically designed to require,
                        such as by intimate data communication or control flow between those
                        subprograms and other parts of the work.
                        
                          The Corresponding Source need not include anything that users
                        can regenerate automatically from other parts of the Corresponding
                        Source.
                        
                          The Corresponding Source for a work in source code form is that
                        same work.
                        
                          2. Basic Permissions.
                        
                          All rights granted under this License are granted for the term of
                        copyright on the Program, and are irrevocable provided the stated
                        conditions are met.  This License explicitly affirms your unlimited
                        permission to run the unmodified Program.  The output from running a
                        covered work is covered by this License only if the output, given its
                        content, constitutes a covered work.  This License acknowledges your
                        rights of fair use or other equivalent, as provided by copyright law.
                        
                          You may make, run and propagate covered works that you do not
                        convey, without conditions so long as your license otherwise remains
                        in force.  You may convey covered works to others for the sole purpose
                        of having them make modifications exclusively for you, or provide you
                        with facilities for running those works, provided that you comply with
                        the terms of this License in conveying all material for which you do
                        not control copyright.  Those thus making or running the covered works
                        for you must do so exclusively on your behalf, under your direction
                        and control, on terms that prohibit them from making any copies of
                        your copyrighted material outside their relationship with you.
                        
                          Conveying under any other circumstances is permitted solely under
                        the conditions stated below.  Sublicensing is not allowed; section 10
                        makes it unnecessary.
                        
                          3. Protecting Users' Legal Rights From Anti-Circumvention Law.
                        
                          No covered work shall be deemed part of an effective technological
                        measure under any applicable law fulfilling obligations under article
                        11 of the WIPO copyright treaty adopted on 20 December 1996, or
                        similar laws prohibiting or restricting circumvention of such
                        measures.
                        
                          When you convey a covered work, you waive any legal power to forbid
                        circumvention of technological measures to the extent such circumvention
                        is effected by exercising rights under this License with respect to
                        the covered work, and you disclaim any intention to limit operation or
                        modification of the work as a means of enforcing, against the work's
                        users, your or third parties' legal rights to forbid circumvention of
                        technological measures.
                        
                          4. Conveying Verbatim Copies.
                        
                          You may convey verbatim copies of the Program's source code as you
                        receive it, in any medium, provided that you conspicuously and
                        appropriately publish on each copy an appropriate copyright notice;
                        keep intact all notices stating that this License and any
                        non-permissive terms added in accord with section 7 apply to the code;
                        keep intact all notices of the absence of any warranty; and give all
                        recipients a copy of this License along with the Program.
                        
                          You may charge any price or no price for each copy that you convey,
                        and you may offer support or warranty protection for a fee.
                        
                          5. Conveying Modified Source Versions.
                        
                          You may convey a work based on the Program, or the modifications to
                        produce it from the Program, in the form of source code under the
                        terms of section 4, provided that you also meet all of these conditions:
                        
                            a) The work must carry prominent notices stating that you modified
                            it, and giving a relevant date.
                        
                            b) The work must carry prominent notices stating that it is
                            released under this License and any conditions added under section
                            7.  This requirement modifies the requirement in section 4 to
                            "keep intact all notices".
                        
                            c) You must license the entire work, as a whole, under this
                            License to anyone who comes into possession of a copy.  This
                            License will therefore apply, along with any applicable section 7
                            additional terms, to the whole of the work, and all its parts,
                            regardless of how they are packaged.  This License gives no
                            permission to license the work in any other way, but it does not
                            invalidate such permission if you have separately received it.
                        
                            d) If the work has interactive user interfaces, each must display
                            Appropriate Legal Notices; however, if the Program has interactive
                            interfaces that do not display Appropriate Legal Notices, your
                            work need not make them do so.
                        
                          A compilation of a covered work with other separate and independent
                        works, which are not by their nature extensions of the covered work,
                        and which are not combined with it such as to form a larger program,
                        in or on a volume of a storage or distribution medium, is called an
                        "aggregate" if the compilation and its resulting copyright are not
                        used to limit the access or legal rights of the compilation's users
                        beyond what the individual works permit.  Inclusion of a covered work
                        in an aggregate does not cause this License to apply to the other
                        parts of the aggregate.
                        
                          6. Conveying Non-Source Forms.
                        
                          You may convey a covered work in object code form under the terms
                        of sections 4 and 5, provided that you also convey the
                        machine-readable Corresponding Source under the terms of this License,
                        in one of these ways:
                        
                            a) Convey the object code in, or embodied in, a physical product
                            (including a physical distribution medium), accompanied by the
                            Corresponding Source fixed on a durable physical medium
                            customarily used for software interchange.
                        
                            b) Convey the object code in, or embodied in, a physical product
                            (including a physical distribution medium), accompanied by a
                            written offer, valid for at least three years and valid for as
                            long as you offer spare parts or customer support for that product
                            model, to give anyone who possesses the object code either (1) a
                            copy of the Corresponding Source for all the software in the
                            product that is covered by this License, on a durable physical
                            medium customarily used for software interchange, for a price no
                            more than your reasonable cost of physically performing this
                            conveying of source, or (2) access to copy the
                            Corresponding Source from a network server at no charge.
                        
                            c) Convey individual copies of the object code with a copy of the
                            written offer to provide the Corresponding Source.  This
                            alternative is allowed only occasionally and noncommercially, and
                            only if you received the object code with such an offer, in accord
                            with subsection 6b.
                        
                            d) Convey the object code by offering access from a designated
                            place (gratis or for a charge), and offer equivalent access to the
                            Corresponding Source in the same way through the same place at no
                            further charge.  You need not require recipients to copy the
                            Corresponding Source along with the object code.  If the place to
                            copy the object code is a network server, the Corresponding Source
                            may be on a different server (operated by you or a third party)
                            that supports equivalent copying facilities, provided you maintain
                            clear directions next to the object code saying where to find the
                            Corresponding Source.  Regardless of what server hosts the
                            Corresponding Source, you remain obligated to ensure that it is
                            available for as long as needed to satisfy these requirements.
                        
                            e) Convey the object code using peer-to-peer transmission, provided
                            you inform other peers where the object code and Corresponding
                            Source of the work are being offered to the general public at no
                            charge under subsection 6d.
                        
                          A separable portion of the object code, whose source code is excluded
                        from the Corresponding Source as a System Library, need not be
                        included in conveying the object code work.
                        
                          A "User Product" is either (1) a "consumer product", which means any
                        tangible personal property which is normally used for personal, family,
                        or household purposes, or (2) anything designed or sold for incorporation
                        into a dwelling.  In determining whether a product is a consumer product,
                        doubtful cases shall be resolved in favor of coverage.  For a particular
                        product received by a particular user, "normally used" refers to a
                        typical or common use of that class of product, regardless of the status
                        of the particular user or of the way in which the particular user
                        actually uses, or expects or is expected to use, the product.  A product
                        is a consumer product regardless of whether the product has substantial
                        commercial, industrial or non-consumer uses, unless such uses represent
                        the only significant mode of use of the product.
                        
                          "Installation Information" for a User Product means any methods,
                        procedures, authorization keys, or other information required to install
                        and execute modified versions of a covered work in that User Product from
                        a modified version of its Corresponding Source.  The information must
                        suffice to ensure that the continued functioning of the modified object
                        code is in no case prevented or interfered with solely because
                        modification has been made.
                        
                          If you convey an object code work under this section in, or with, or
                        specifically for use in, a User Product, and the conveying occurs as
                        part of a transaction in which the right of possession and use of the
                        User Product is transferred to the recipient in perpetuity or for a
                        fixed term (regardless of how the transaction is characterized), the
                        Corresponding Source conveyed under this section must be accompanied
                        by the Installation Information.  But this requirement does not apply
                        if neither you nor any third party retains the ability to install
                        modified object code on the User Product (for example, the work has
                        been installed in ROM).
                        
                          The requirement to provide Installation Information does not include a
                        requirement to continue to provide support service, warranty, or updates
                        for a work that has been modified or installed by the recipient, or for
                        the User Product in which it has been modified or installed.  Access to a
                        network may be denied when the modification itself materially and
                        adversely affects the operation of the network or violates the rules and
                        protocols for communication across the network.
                        
                          Corresponding Source conveyed, and Installation Information provided,
                        in accord with this section must be in a format that is publicly
                        documented (and with an implementation available to the public in
                        source code form), and must require no special password or key for
                        unpacking, reading or copying.
                        
                          7. Additional Terms.
                        
                          "Additional permissions" are terms that supplement the terms of this
                        License by making exceptions from one or more of its conditions.
                        Additional permissions that are applicable to the entire Program shall
                        be treated as though they were included in this License, to the extent
                        that they are valid under applicable law.  If additional permissions
                        apply only to part of the Program, that part may be used separately
                        under those permissions, but the entire Program remains governed by
                        this License without regard to the additional permissions.
                        
                          When you convey a copy of a covered work, you may at your option
                        remove any additional permissions from that copy, or from any part of
                        it.  (Additional permissions may be written to require their own
                        removal in certain cases when you modify the work.)  You may place
                        additional permissions on material, added by you to a covered work,
                        for which you have or can give appropriate copyright permission.
                        
                          Notwithstanding any other provision of this License, for material you
                        add to a covered work, you may (if authorized by the copyright holders of
                        that material) supplement the terms of this License with terms:
                        
                            a) Disclaiming warranty or limiting liability differently from the
                            terms of sections 15 and 16 of this License; or
                        
                            b) Requiring preservation of specified reasonable legal notices or
                            author attributions in that material or in the Appropriate Legal
                            Notices displayed by works containing it; or
                        
                            c) Prohibiting misrepresentation of the origin of that material, or
                            requiring that modified versions of such material be marked in
                            reasonable ways as different from the original version; or
                        
                            d) Limiting the use for publicity purposes of names of licensors or
                            authors of the material; or
                        
                            e) Declining to grant rights under trademark law for use of some
                            trade names, trademarks, or service marks; or
                        
                            f) Requiring indemnification of licensors and authors of that
                            material by anyone who conveys the material (or modified versions of
                            it) with contractual assumptions of liability to the recipient, for
                            any liability that these contractual assumptions directly impose on
                            those licensors and authors.
                        
                          All other non-permissive additional terms are considered "further
                        restrictions" within the meaning of section 10.  If the Program as you
                        received it, or any part of it, contains a notice stating that it is
                        governed by this License along with a term that is a further
                        restriction, you may remove that term.  If a license document contains
                        a further restriction but permits relicensing or conveying under this
                        License, you may add to a covered work material governed by the terms
                        of that license document, provided that the further restriction does
                        not survive such relicensing or conveying.
                        
                          If you add terms to a covered work in accord with this section, you
                        must place, in the relevant source files, a statement of the
                        additional terms that apply to those files, or a notice indicating
                        where to find the applicable terms.
                        
                          Additional terms, permissive or non-permissive, may be stated in the
                        form of a separately written license, or stated as exceptions;
                        the above requirements apply either way.
                        
                          8. Termination.
                        
                          You may not propagate or modify a covered work except as expressly
                        provided under this License.  Any attempt otherwise to propagate or
                        modify it is void, and will automatically terminate your rights under
                        this License (including any patent licenses granted under the third
                        paragraph of section 11).
                        
                          However, if you cease all violation of this License, then your
                        license from a particular copyright holder is reinstated (a)
                        provisionally, unless and until the copyright holder explicitly and
                        finally terminates your license, and (b) permanently, if the copyright
                        holder fails to notify you of the violation by some reasonable means
                        prior to 60 days after the cessation.
                        
                          Moreover, your license from a particular copyright holder is
                        reinstated permanently if the copyright holder notifies you of the
                        violation by some reasonable means, this is the first time you have
                        received notice of violation of this License (for any work) from that
                        copyright holder, and you cure the violation prior to 30 days after
                        your receipt of the notice.
                        
                          Termination of your rights under this section does not terminate the
                        licenses of parties who have received copies or rights from you under
                        this License.  If your rights have been terminated and not permanently
                        reinstated, you do not qualify to receive new licenses for the same
                        material under section 10.
                        
                          9. Acceptance Not Required for Having Copies.
                        
                          You are not required to accept this License in order to receive or
                        run a copy of the Program.  Ancillary propagation of a covered work
                        occurring solely as a consequence of using peer-to-peer transmission
                        to receive a copy likewise does not require acceptance.  However,
                        nothing other than this License grants you permission to propagate or
                        modify any covered work.  These actions infringe copyright if you do
                        not accept this License.  Therefore, by modifying or propagating a
                        covered work, you indicate your acceptance of this License to do so.
                        
                          10. Automatic Licensing of Downstream Recipients.
                        
                          Each time you convey a covered work, the recipient automatically
                        receives a license from the original licensors, to run, modify and
                        propagate that work, subject to this License.  You are not responsible
                        for enforcing compliance by third parties with this License.
                        
                          An "entity transaction" is a transaction transferring control of an
                        organization, or substantially all assets of one, or subdividing an
                        organization, or merging organizations.  If propagation of a covered
                        work results from an entity transaction, each party to that
                        transaction who receives a copy of the work also receives whatever
                        licenses to the work the party's predecessor in interest had or could
                        give under the previous paragraph, plus a right to possession of the
                        Corresponding Source of the work from the predecessor in interest, if
                        the predecessor has it or can get it with reasonable efforts.
                        
                          You may not impose any further restrictions on the exercise of the
                        rights granted or affirmed under this License.  For example, you may
                        not impose a license fee, royalty, or other charge for exercise of
                        rights granted under this License, and you may not initiate litigation
                        (including a cross-claim or counterclaim in a lawsuit) alleging that
                        any patent claim is infringed by making, using, selling, offering for
                        sale, or importing the Program or any portion of it.
                        
                          11. Patents.
                        
                          A "contributor" is a copyright holder who authorizes use under this
                        License of the Program or a work on which the Program is based.  The
                        work thus licensed is called the contributor's "contributor version".
                        
                          A contributor's "essential patent claims" are all patent claims
                        owned or controlled by the contributor, whether already acquired or
                        hereafter acquired, that would be infringed by some manner, permitted
                        by this License, of making, using, or selling its contributor version,
                        but do not include claims that would be infringed only as a
                        consequence of further modification of the contributor version.  For
                        purposes of this definition, "control" includes the right to grant
                        patent sublicenses in a manner consistent with the requirements of
                        this License.
                        
                          Each contributor grants you a non-exclusive, worldwide, royalty-free
                        patent license under the contributor's essential patent claims, to
                        make, use, sell, offer for sale, import and otherwise run, modify and
                        propagate the contents of its contributor version.
                        
                          In the following three paragraphs, a "patent license" is any express
                        agreement or commitment, however denominated, not to enforce a patent
                        (such as an express permission to practice a patent or covenant not to
                        sue for patent infringement).  To "grant" such a patent license to a
                        party means to make such an agreement or commitment not to enforce a
                        patent against the party.
                        
                          If you convey a covered work, knowingly relying on a patent license,
                        and the Corresponding Source of the work is not available for anyone
                        to copy, free of charge and under the terms of this License, through a
                        publicly available network server or other readily accessible means,
                        then you must either (1) cause the Corresponding Source to be so
                        available, or (2) arrange to deprive yourself of the benefit of the
                        patent license for this particular work, or (3) arrange, in a manner
                        consistent with the requirements of this License, to extend the patent
                        license to downstream recipients.  "Knowingly relying" means you have
                        actual knowledge that, but for the patent license, your conveying the
                        covered work in a country, or your recipient's use of the covered work
                        in a country, would infringe one or more identifiable patents in that
                        country that you have reason to believe are valid.
                        
                          If, pursuant to or in connection with a single transaction or
                        arrangement, you convey, or propagate by procuring conveyance of, a
                        covered work, and grant a patent license to some of the parties
                        receiving the covered work authorizing them to use, propagate, modify
                        or convey a specific copy of the covered work, then the patent license
                        you grant is automatically extended to all recipients of the covered
                        work and works based on it.
                        
                          A patent license is "discriminatory" if it does not include within
                        the scope of its coverage, prohibits the exercise of, or is
                        conditioned on the non-exercise of one or more of the rights that are
                        specifically granted under this License.  You may not convey a covered
                        work if you are a party to an arrangement with a third party that is
                        in the business of distributing software, under which you make payment
                        to the third party based on the extent of your activity of conveying
                        the work, and under which the third party grants, to any of the
                        parties who would receive the covered work from you, a discriminatory
                        patent license (a) in connection with copies of the covered work
                        conveyed by you (or copies made from those copies), or (b) primarily
                        for and in connection with specific products or compilations that
                        contain the covered work, unless you entered into that arrangement,
                        or that patent license was granted, prior to 28 March 2007.
                        
                          Nothing in this License shall be construed as excluding or limiting
                        any implied license or other defenses to infringement that may
                        otherwise be available to you under applicable patent law.
                        
                          12. No Surrender of Others' Freedom.
                        
                          If conditions are imposed on you (whether by court order, agreement or
                        otherwise) that contradict the conditions of this License, they do not
                        excuse you from the conditions of this License.  If you cannot convey a
                        covered work so as to satisfy simultaneously your obligations under this
                        License and any other pertinent obligations, then as a consequence you may
                        not convey it at all.  For example, if you agree to terms that obligate you
                        to collect a royalty for further conveying from those to whom you convey
                        the Program, the only way you could satisfy both those terms and this
                        License would be to refrain entirely from conveying the Program.
                        
                          13. Use with the GNU Affero General Public License.
                        
                          Notwithstanding any other provision of this License, you have
                        permission to link or combine any covered work with a work licensed
                        under version 3 of the GNU Affero General Public License into a single
                        combined work, and to convey the resulting work.  The terms of this
                        License will continue to apply to the part which is the covered work,
                        but the special requirements of the GNU Affero General Public License,
                        section 13, concerning interaction through a network will apply to the
                        combination as such.
                        
                          14. Revised Versions of this License.
                        
                          The Free Software Foundation may publish revised and/or new versions of
                        the GNU General Public License from time to time.  Such new versions will
                        be similar in spirit to the present version, but may differ in detail to
                        address new problems or concerns.
                        
                          Each version is given a distinguishing version number.  If the
                        Program specifies that a certain numbered version of the GNU General
                        Public License "or any later version" applies to it, you have the
                        option of following the terms and conditions either of that numbered
                        version or of any later version published by the Free Software
                        Foundation.  If the Program does not specify a version number of the
                        GNU General Public License, you may choose any version ever published
                        by the Free Software Foundation.
                        
                          If the Program specifies that a proxy can decide which future
                        versions of the GNU General Public License can be used, that proxy's
                        public statement of acceptance of a version permanently authorizes you
                        to choose that version for the Program.
                        
                          Later license versions may give you additional or different
                        permissions.  However, no additional obligations are imposed on any
                        author or copyright holder as a result of your choosing to follow a
                        later version.
                        
                          15. Disclaimer of Warranty.
                        
                          THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
                        APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
                        HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
                        OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
                        THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                        PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
                        IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
                        ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
                        
                          16. Limitation of Liability.
                        
                          IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
                        WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
                        THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
                        GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
                        USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
                        DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
                        PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
                        EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
                        SUCH DAMAGES.
                        
                          17. Interpretation of Sections 15 and 16.
                        
                          If the disclaimer of warranty and limitation of liability provided
                        above cannot be given local legal effect according to their terms,
                        reviewing courts shall apply local law that most closely approximates
                        an absolute waiver of all civil liability in connection with the
                        Program, unless a warranty or assumption of liability accompanies a
                        copy of the Program in return for a fee.
                        
                                             END OF TERMS AND CONDITIONS
                        
                                    How to Apply These Terms to Your New Programs
                        
                          If you develop a new program, and you want it to be of the greatest
                        possible use to the public, the best way to achieve this is to make it
                        free software which everyone can redistribute and change under these terms.
                        
                          To do so, attach the following notices to the program.  It is safest
                        to attach them to the start of each source file to most effectively
                        state the exclusion of warranty; and each file should have at least
                        the "copyright" line and a pointer to where the full notice is found.
                        
                            <one line to give the program's name and a brief idea of what it does.>
                            Copyright (C) <year>  <name of author>
                        
                            This program is free software: you can redistribute it and/or modify
                            it under the terms of the GNU General Public License as published by
                            the Free Software Foundation, either version 3 of the License, or
                            (at your option) any later version.
                        
                            This program is distributed in the hope that it will be useful,
                            but WITHOUT ANY WARRANTY; without even the implied warranty of
                            MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                            GNU General Public License for more details.
                        
                            You should have received a copy of the GNU General Public License
                            along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        
                        Also add information on how to contact you by electronic and paper mail.
                        
                          If the program does terminal interaction, make it output a short
                        notice like this when it starts in an interactive mode:
                        
                            <program>  Copyright (C) <year>  <name of author>
                            This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
                            This is free software, and you are welcome to redistribute it
                            under certain conditions; type `show c' for details.
                        
                        The hypothetical commands `show w' and `show c' should show the appropriate
                        parts of the General Public License.  Of course, your program's commands
                        might be different; for a GUI interface, you would use an "about box".
                        
                          You should also get your employer (if you work as a programmer) or school,
                        if any, to sign a "copyright disclaimer" for the program, if necessary.
                        For more information on this, and how to apply and follow the GNU GPL, see
                        <http://www.gnu.org/licenses/>.
                        
                          The GNU General Public License does not permit incorporating your program
                        into proprietary programs.  If your program is a subroutine library, you
                        may consider it more useful to permit linking proprietary applications with
                        the library.  If this is what you want to do, use the GNU Lesser General
                        Public License instead of this License.  But first, please read
                        <http://www.gnu.org/philosophy/why-not-lgpl.html>.
                        
                        */

                        File 4 of 11: Proxy
                        pragma solidity ^0.5.3;
                        
                        /// @title Proxy - Generic proxy contract allows to execute all transactions applying the code of a master contract.
                        /// @author Stefan George - <[email protected]>
                        /// @author Richard Meissner - <[email protected]>
                        contract Proxy {
                        
                            // masterCopy always needs to be first declared variable, to ensure that it is at the same location in the contracts to which calls are delegated.
                            // To reduce deployment costs this variable is internal and needs to be retrieved via `getStorageAt`
                            address internal masterCopy;
                        
                            /// @dev Constructor function sets address of master copy contract.
                            /// @param _masterCopy Master copy address.
                            constructor(address _masterCopy)
                                public
                            {
                                require(_masterCopy != address(0), "Invalid master copy address provided");
                                masterCopy = _masterCopy;
                            }
                        
                            /// @dev Fallback function forwards all transactions and returns all received return data.
                            function ()
                                external
                                payable
                            {
                                // solium-disable-next-line security/no-inline-assembly
                                assembly {
                                    let masterCopy := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)
                                    // 0xa619486e == keccak("masterCopy()"). The value is right padded to 32-bytes with 0s
                                    if eq(calldataload(0), 0xa619486e00000000000000000000000000000000000000000000000000000000) {
                                        mstore(0, masterCopy)
                                        return(0, 0x20)
                                    }
                                    calldatacopy(0, 0, calldatasize())
                                    let success := delegatecall(gas, masterCopy, 0, calldatasize(), 0, 0)
                                    returndatacopy(0, 0, returndatasize())
                                    if eq(success, 0) { revert(0, returndatasize()) }
                                    return(0, returndatasize())
                                }
                            }
                        }

                        File 5 of 11: DropERC721
                        // SPDX-License-Identifier: MIT
                        // ERC721A Contracts v3.3.0
                        // Creator: Chiru Labs
                        ////////// CHANGELOG: turn `approve` to virtual //////////
                        pragma solidity ^0.8.4;
                        import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol";
                        import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol";
                        import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
                        import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
                        import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";
                        import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol";
                        import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
                        /**
                         * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
                         * the Metadata extension. Built to optimize for lower gas during batch mints.
                         *
                         * Assumes serials are sequentially minted starting at _startTokenId() (defaults to 0, e.g. 0, 1, 2, 3..).
                         *
                         * Assumes that an owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
                         *
                         * Assumes that the maximum token id cannot exceed 2**256 - 1 (max value of uint256).
                         */
                        contract ERC721AUpgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721AUpgradeable {
                            using AddressUpgradeable for address;
                            using StringsUpgradeable for uint256;
                            // The tokenId of the next token to be minted.
                            uint256 internal _currentIndex;
                            // The number of tokens burned.
                            uint256 internal _burnCounter;
                            // Token name
                            string private _name;
                            // Token symbol
                            string private _symbol;
                            // Mapping from token ID to ownership details
                            // An empty struct value does not necessarily mean the token is unowned. See _ownershipOf implementation for details.
                            mapping(uint256 => TokenOwnership) internal _ownerships;
                            // Mapping owner address to address data
                            mapping(address => AddressData) private _addressData;
                            // Mapping from token ID to approved address
                            mapping(uint256 => address) private _tokenApprovals;
                            // Mapping from owner to operator approvals
                            mapping(address => mapping(address => bool)) private _operatorApprovals;
                            function __ERC721A_init(string memory name_, string memory symbol_) internal onlyInitializing {
                                __ERC721A_init_unchained(name_, symbol_);
                            }
                            function __ERC721A_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
                                _name = name_;
                                _symbol = symbol_;
                                _currentIndex = _startTokenId();
                            }
                            /**
                             * To change the starting tokenId, please override this function.
                             */
                            function _startTokenId() internal view virtual returns (uint256) {
                                return 0;
                            }
                            /**
                             * @dev Burned tokens are calculated here, use _totalMinted() if you want to count just minted tokens.
                             */
                            function totalSupply() public view override returns (uint256) {
                                // Counter underflow is impossible as _burnCounter cannot be incremented
                                // more than _currentIndex - _startTokenId() times
                                unchecked {
                                    return _currentIndex - _burnCounter - _startTokenId();
                                }
                            }
                            /**
                             * Returns the total amount of tokens minted in the contract.
                             */
                            function _totalMinted() internal view returns (uint256) {
                                // Counter underflow is impossible as _currentIndex does not decrement,
                                // and it is initialized to _startTokenId()
                                unchecked {
                                    return _currentIndex - _startTokenId();
                                }
                            }
                            /**
                             * @dev See {IERC165-supportsInterface}.
                             */
                            function supportsInterface(
                                bytes4 interfaceId
                            ) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) {
                                return
                                    interfaceId == type(IERC721Upgradeable).interfaceId ||
                                    interfaceId == type(IERC721MetadataUpgradeable).interfaceId ||
                                    super.supportsInterface(interfaceId);
                            }
                            /**
                             * @dev See {IERC721-balanceOf}.
                             */
                            function balanceOf(address owner) public view override returns (uint256) {
                                if (owner == address(0)) revert BalanceQueryForZeroAddress();
                                return uint256(_addressData[owner].balance);
                            }
                            /**
                             * Returns the number of tokens minted by `owner`.
                             */
                            function _numberMinted(address owner) internal view returns (uint256) {
                                return uint256(_addressData[owner].numberMinted);
                            }
                            /**
                             * Returns the number of tokens burned by or on behalf of `owner`.
                             */
                            function _numberBurned(address owner) internal view returns (uint256) {
                                return uint256(_addressData[owner].numberBurned);
                            }
                            /**
                             * Returns the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
                             */
                            function _getAux(address owner) internal view returns (uint64) {
                                return _addressData[owner].aux;
                            }
                            /**
                             * Sets the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
                             * If there are multiple variables, please pack them into a uint64.
                             */
                            function _setAux(address owner, uint64 aux) internal {
                                _addressData[owner].aux = aux;
                            }
                            /**
                             * Gas spent here starts off proportional to the maximum mint batch size.
                             * It gradually moves to O(1) as tokens get transferred around in the collection over time.
                             */
                            function _ownershipOf(uint256 tokenId) internal view returns (TokenOwnership memory) {
                                uint256 curr = tokenId;
                                unchecked {
                                    if (_startTokenId() <= curr)
                                        if (curr < _currentIndex) {
                                            TokenOwnership memory ownership = _ownerships[curr];
                                            if (!ownership.burned) {
                                                if (ownership.addr != address(0)) {
                                                    return ownership;
                                                }
                                                // Invariant:
                                                // There will always be an ownership that has an address and is not burned
                                                // before an ownership that does not have an address and is not burned.
                                                // Hence, curr will not underflow.
                                                while (true) {
                                                    curr--;
                                                    ownership = _ownerships[curr];
                                                    if (ownership.addr != address(0)) {
                                                        return ownership;
                                                    }
                                                }
                                            }
                                        }
                                }
                                revert OwnerQueryForNonexistentToken();
                            }
                            /**
                             * @dev See {IERC721-ownerOf}.
                             */
                            function ownerOf(uint256 tokenId) public view override returns (address) {
                                return _ownershipOf(tokenId).addr;
                            }
                            /**
                             * @dev See {IERC721Metadata-name}.
                             */
                            function name() public view virtual override returns (string memory) {
                                return _name;
                            }
                            /**
                             * @dev See {IERC721Metadata-symbol}.
                             */
                            function symbol() public view virtual override returns (string memory) {
                                return _symbol;
                            }
                            /**
                             * @dev See {IERC721Metadata-tokenURI}.
                             */
                            function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
                                if (!_exists(tokenId)) revert URIQueryForNonexistentToken();
                                string memory baseURI = _baseURI();
                                return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
                            }
                            /**
                             * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
                             * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
                             * by default, can be overriden in child contracts.
                             */
                            function _baseURI() internal view virtual returns (string memory) {
                                return "";
                            }
                            /**
                             * @dev See {IERC721-approve}.
                             */
                            function approve(address to, uint256 tokenId) public virtual override {
                                address owner = ERC721AUpgradeable.ownerOf(tokenId);
                                if (to == owner) revert ApprovalToCurrentOwner();
                                if (_msgSender() != owner)
                                    if (!isApprovedForAll(owner, _msgSender())) {
                                        revert ApprovalCallerNotOwnerNorApproved();
                                    }
                                _approve(to, tokenId, owner);
                            }
                            /**
                             * @dev See {IERC721-getApproved}.
                             */
                            function getApproved(uint256 tokenId) public view override returns (address) {
                                if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();
                                return _tokenApprovals[tokenId];
                            }
                            /**
                             * @dev See {IERC721-setApprovalForAll}.
                             */
                            function setApprovalForAll(address operator, bool approved) public virtual override {
                                if (operator == _msgSender()) revert ApproveToCaller();
                                _operatorApprovals[_msgSender()][operator] = approved;
                                emit ApprovalForAll(_msgSender(), operator, approved);
                            }
                            /**
                             * @dev See {IERC721-isApprovedForAll}.
                             */
                            function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
                                return _operatorApprovals[owner][operator];
                            }
                            /**
                             * @dev See {IERC721-transferFrom}.
                             */
                            function transferFrom(address from, address to, uint256 tokenId) public virtual override {
                                _transfer(from, to, tokenId);
                            }
                            /**
                             * @dev See {IERC721-safeTransferFrom}.
                             */
                            function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
                                safeTransferFrom(from, to, tokenId, "");
                            }
                            /**
                             * @dev See {IERC721-safeTransferFrom}.
                             */
                            function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override {
                                _transfer(from, to, tokenId);
                                if (to.isContract())
                                    if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
                                        revert TransferToNonERC721ReceiverImplementer();
                                    }
                            }
                            /**
                             * @dev Returns whether `tokenId` exists.
                             *
                             * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
                             *
                             * Tokens start existing when they are minted (`_mint`),
                             */
                            function _exists(uint256 tokenId) internal view returns (bool) {
                                return _startTokenId() <= tokenId && tokenId < _currentIndex && !_ownerships[tokenId].burned;
                            }
                            /**
                             * @dev Equivalent to `_safeMint(to, quantity, '')`.
                             */
                            function _safeMint(address to, uint256 quantity) internal {
                                _safeMint(to, quantity, "");
                            }
                            /**
                             * @dev Safely mints `quantity` tokens and transfers them to `to`.
                             *
                             * Requirements:
                             *
                             * - If `to` refers to a smart contract, it must implement
                             *   {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
                             * - `quantity` must be greater than 0.
                             *
                             * Emits a {Transfer} event.
                             */
                            function _safeMint(address to, uint256 quantity, bytes memory _data) internal {
                                uint256 startTokenId = _currentIndex;
                                if (to == address(0)) revert MintToZeroAddress();
                                if (quantity == 0) revert MintZeroQuantity();
                                _beforeTokenTransfers(address(0), to, startTokenId, quantity);
                                // Overflows are incredibly unrealistic.
                                // balance or numberMinted overflow if current value of either + quantity > 1.8e19 (2**64) - 1
                                // updatedIndex overflows if _currentIndex + quantity > 1.2e77 (2**256) - 1
                                unchecked {
                                    _addressData[to].balance += uint64(quantity);
                                    _addressData[to].numberMinted += uint64(quantity);
                                    _ownerships[startTokenId].addr = to;
                                    _ownerships[startTokenId].startTimestamp = uint64(block.timestamp);
                                    uint256 updatedIndex = startTokenId;
                                    uint256 end = updatedIndex + quantity;
                                    if (to.isContract()) {
                                        do {
                                            emit Transfer(address(0), to, updatedIndex);
                                            if (!_checkContractOnERC721Received(address(0), to, updatedIndex, _data)) {
                                                revert TransferToNonERC721ReceiverImplementer();
                                            }
                                            updatedIndex += 1;
                                        } while (updatedIndex < end);
                                        // Reentrancy protection
                                        if (_currentIndex != startTokenId) revert();
                                    } else {
                                        do {
                                            emit Transfer(address(0), to, updatedIndex++);
                                        } while (updatedIndex < end);
                                    }
                                    _currentIndex = updatedIndex;
                                }
                                _afterTokenTransfers(address(0), to, startTokenId, quantity);
                            }
                            /**
                             * @dev Mints `quantity` tokens and transfers them to `to`.
                             *
                             * Requirements:
                             *
                             * - `to` cannot be the zero address.
                             * - `quantity` must be greater than 0.
                             *
                             * Emits a {Transfer} event.
                             */
                            function _mint(address to, uint256 quantity) internal {
                                uint256 startTokenId = _currentIndex;
                                if (to == address(0)) revert MintToZeroAddress();
                                if (quantity == 0) revert MintZeroQuantity();
                                _beforeTokenTransfers(address(0), to, startTokenId, quantity);
                                // Overflows are incredibly unrealistic.
                                // balance or numberMinted overflow if current value of either + quantity > 1.8e19 (2**64) - 1
                                // updatedIndex overflows if _currentIndex + quantity > 1.2e77 (2**256) - 1
                                unchecked {
                                    _addressData[to].balance += uint64(quantity);
                                    _addressData[to].numberMinted += uint64(quantity);
                                    _ownerships[startTokenId].addr = to;
                                    _ownerships[startTokenId].startTimestamp = uint64(block.timestamp);
                                    uint256 updatedIndex = startTokenId;
                                    uint256 end = updatedIndex + quantity;
                                    do {
                                        emit Transfer(address(0), to, updatedIndex++);
                                    } while (updatedIndex < end);
                                    _currentIndex = updatedIndex;
                                }
                                _afterTokenTransfers(address(0), to, startTokenId, quantity);
                            }
                            /**
                             * @dev Transfers `tokenId` from `from` to `to`.
                             *
                             * Requirements:
                             *
                             * - `to` cannot be the zero address.
                             * - `tokenId` token must be owned by `from`.
                             *
                             * Emits a {Transfer} event.
                             */
                            function _transfer(address from, address to, uint256 tokenId) private {
                                TokenOwnership memory prevOwnership = _ownershipOf(tokenId);
                                if (prevOwnership.addr != from) revert TransferFromIncorrectOwner();
                                bool isApprovedOrOwner = (_msgSender() == from ||
                                    isApprovedForAll(from, _msgSender()) ||
                                    getApproved(tokenId) == _msgSender());
                                if (!isApprovedOrOwner) revert TransferCallerNotOwnerNorApproved();
                                if (to == address(0)) revert TransferToZeroAddress();
                                _beforeTokenTransfers(from, to, tokenId, 1);
                                // Clear approvals from the previous owner
                                _approve(address(0), tokenId, from);
                                // Underflow of the sender's balance is impossible because we check for
                                // ownership above and the recipient's balance can't realistically overflow.
                                // Counter overflow is incredibly unrealistic as tokenId would have to be 2**256.
                                unchecked {
                                    _addressData[from].balance -= 1;
                                    _addressData[to].balance += 1;
                                    TokenOwnership storage currSlot = _ownerships[tokenId];
                                    currSlot.addr = to;
                                    currSlot.startTimestamp = uint64(block.timestamp);
                                    // If the ownership slot of tokenId+1 is not explicitly set, that means the transfer initiator owns it.
                                    // Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.
                                    uint256 nextTokenId = tokenId + 1;
                                    TokenOwnership storage nextSlot = _ownerships[nextTokenId];
                                    if (nextSlot.addr == address(0)) {
                                        // This will suffice for checking _exists(nextTokenId),
                                        // as a burned slot cannot contain the zero address.
                                        if (nextTokenId != _currentIndex) {
                                            nextSlot.addr = from;
                                            nextSlot.startTimestamp = prevOwnership.startTimestamp;
                                        }
                                    }
                                }
                                emit Transfer(from, to, tokenId);
                                _afterTokenTransfers(from, to, tokenId, 1);
                            }
                            /**
                             * @dev Equivalent to `_burn(tokenId, false)`.
                             */
                            function _burn(uint256 tokenId) internal virtual {
                                _burn(tokenId, false);
                            }
                            /**
                             * @dev Destroys `tokenId`.
                             * The approval is cleared when the token is burned.
                             *
                             * Requirements:
                             *
                             * - `tokenId` must exist.
                             *
                             * Emits a {Transfer} event.
                             */
                            function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
                                TokenOwnership memory prevOwnership = _ownershipOf(tokenId);
                                address from = prevOwnership.addr;
                                if (approvalCheck) {
                                    bool isApprovedOrOwner = (_msgSender() == from ||
                                        isApprovedForAll(from, _msgSender()) ||
                                        getApproved(tokenId) == _msgSender());
                                    if (!isApprovedOrOwner) revert TransferCallerNotOwnerNorApproved();
                                }
                                _beforeTokenTransfers(from, address(0), tokenId, 1);
                                // Clear approvals from the previous owner
                                _approve(address(0), tokenId, from);
                                // Underflow of the sender's balance is impossible because we check for
                                // ownership above and the recipient's balance can't realistically overflow.
                                // Counter overflow is incredibly unrealistic as tokenId would have to be 2**256.
                                unchecked {
                                    AddressData storage addressData = _addressData[from];
                                    addressData.balance -= 1;
                                    addressData.numberBurned += 1;
                                    // Keep track of who burned the token, and the timestamp of burning.
                                    TokenOwnership storage currSlot = _ownerships[tokenId];
                                    currSlot.addr = from;
                                    currSlot.startTimestamp = uint64(block.timestamp);
                                    currSlot.burned = true;
                                    // If the ownership slot of tokenId+1 is not explicitly set, that means the burn initiator owns it.
                                    // Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.
                                    uint256 nextTokenId = tokenId + 1;
                                    TokenOwnership storage nextSlot = _ownerships[nextTokenId];
                                    if (nextSlot.addr == address(0)) {
                                        // This will suffice for checking _exists(nextTokenId),
                                        // as a burned slot cannot contain the zero address.
                                        if (nextTokenId != _currentIndex) {
                                            nextSlot.addr = from;
                                            nextSlot.startTimestamp = prevOwnership.startTimestamp;
                                        }
                                    }
                                }
                                emit Transfer(from, address(0), tokenId);
                                _afterTokenTransfers(from, address(0), tokenId, 1);
                                // Overflow not possible, as _burnCounter cannot be exceed _currentIndex times.
                                unchecked {
                                    _burnCounter++;
                                }
                            }
                            /**
                             * @dev Approve `to` to operate on `tokenId`
                             *
                             * Emits a {Approval} event.
                             */
                            function _approve(address to, uint256 tokenId, address owner) private {
                                _tokenApprovals[tokenId] = to;
                                emit Approval(owner, to, tokenId);
                            }
                            /**
                             * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target contract.
                             *
                             * @param from address representing the previous owner of the given token ID
                             * @param to target address that will receive the tokens
                             * @param tokenId uint256 ID of the token to be transferred
                             * @param _data bytes optional data to send along with the call
                             * @return bool whether the call correctly returned the expected magic value
                             */
                            function _checkContractOnERC721Received(
                                address from,
                                address to,
                                uint256 tokenId,
                                bytes memory _data
                            ) private returns (bool) {
                                try IERC721ReceiverUpgradeable(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (
                                    bytes4 retval
                                ) {
                                    return retval == IERC721ReceiverUpgradeable(to).onERC721Received.selector;
                                } catch (bytes memory reason) {
                                    if (reason.length == 0) {
                                        revert TransferToNonERC721ReceiverImplementer();
                                    } else {
                                        assembly {
                                            revert(add(32, reason), mload(reason))
                                        }
                                    }
                                }
                            }
                            /**
                             * @dev Hook that is called before a set of serially-ordered token ids are about to be transferred. This includes minting.
                             * And also called before burning one token.
                             *
                             * startTokenId - the first token id to be transferred
                             * quantity - the amount to be transferred
                             *
                             * Calling conditions:
                             *
                             * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
                             * transferred to `to`.
                             * - When `from` is zero, `tokenId` will be minted for `to`.
                             * - When `to` is zero, `tokenId` will be burned by `from`.
                             * - `from` and `to` are never both zero.
                             */
                            function _beforeTokenTransfers(address from, address to, uint256 startTokenId, uint256 quantity) internal virtual {}
                            /**
                             * @dev Hook that is called after a set of serially-ordered token ids have been transferred. This includes
                             * minting.
                             * And also called after one token has been burned.
                             *
                             * startTokenId - the first token id to be transferred
                             * quantity - the amount to be transferred
                             *
                             * Calling conditions:
                             *
                             * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
                             * transferred to `to`.
                             * - When `from` is zero, `tokenId` has been minted for `to`.
                             * - When `to` is zero, `tokenId` has been burned by `from`.
                             * - `from` and `to` are never both zero.
                             */
                            function _afterTokenTransfers(address from, address to, uint256 startTokenId, uint256 quantity) internal virtual {}
                            /**
                             * @dev This empty reserved space is put in place to allow future versions to add new
                             * variables without shifting down storage in the inheritance chain.
                             * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                             */
                            uint256[42] private __gap;
                        }
                        // 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
                         * [EIP](https://eips.ethereum.org/EIPS/eip-165).
                         *
                         * 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
                             * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
                             * 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: Apache-2.0
                        pragma solidity ^0.8.0;
                        /**
                         * @title ERC20 interface
                         * @dev see https://github.com/ethereum/EIPs/issues/20
                         */
                        interface IERC20 {
                            function totalSupply() external view returns (uint256);
                            function balanceOf(address who) external view returns (uint256);
                            function allowance(address owner, address spender) external view returns (uint256);
                            function transfer(address to, uint256 value) external returns (bool);
                            function approve(address spender, uint256 value) external returns (bool);
                            function transferFrom(address from, address to, uint256 value) external returns (bool);
                            event Transfer(address indexed from, address indexed to, uint256 value);
                            event Approval(address indexed owner, address indexed spender, uint256 value);
                        }
                        // SPDX-License-Identifier: Apache 2.0
                        pragma solidity ^0.8.0;
                        import "./IERC165.sol";
                        /**
                         * @dev Interface for the NFT Royalty Standard.
                         *
                         * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
                         * support for royalty payments across all NFT marketplaces and ecosystem participants.
                         *
                         * _Available since v4.5._
                         */
                        interface IERC2981 is IERC165 {
                            /**
                             * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
                             * exchange. The royalty amount is denominated and should be payed in that same unit of exchange.
                             */
                            function royaltyInfo(
                                uint256 tokenId,
                                uint256 salePrice
                            ) external view returns (address receiver, uint256 royaltyAmount);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        /**
                         *  @title   Batch-mint Metadata
                         *  @notice  The `BatchMintMetadata` is a contract extension for any base NFT contract. It lets the smart contract
                         *           using this extension set metadata for `n` number of NFTs all at once. This is enabled by storing a single
                         *           base URI for a batch of `n` NFTs, where the metadata for each NFT in a relevant batch is `baseURI/tokenId`.
                         */
                        contract BatchMintMetadata {
                            /// @dev Invalid index for batch
                            error BatchMintInvalidBatchId(uint256 index);
                            /// @dev Invalid token
                            error BatchMintInvalidTokenId(uint256 tokenId);
                            /// @dev Metadata frozen
                            error BatchMintMetadataFrozen(uint256 batchId);
                            /// @dev Largest tokenId of each batch of tokens with the same baseURI + 1 {ex: batchId 100 at position 0 includes tokens 0-99}
                            uint256[] private batchIds;
                            /// @dev Mapping from id of a batch of tokens => to base URI for the respective batch of tokens.
                            mapping(uint256 => string) private baseURI;
                            /// @dev Mapping from id of a batch of tokens => to whether the base URI for the respective batch of tokens is frozen.
                            mapping(uint256 => bool) public batchFrozen;
                            /// @dev This event emits when the metadata of all tokens are frozen.
                            /// While not currently supported by marketplaces, this event allows
                            /// future indexing if desired.
                            event MetadataFrozen();
                            // @dev This event emits when the metadata of a range of tokens is updated.
                            /// So that the third-party platforms such as NFT market could
                            /// timely update the images and related attributes of the NFTs.
                            event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
                            /**
                             *  @notice         Returns the count of batches of NFTs.
                             *  @dev            Each batch of tokens has an in ID and an associated `baseURI`.
                             *                  See {batchIds}.
                             */
                            function getBaseURICount() public view returns (uint256) {
                                return batchIds.length;
                            }
                            /**
                             *  @notice         Returns the ID for the batch of tokens at the given index.
                             *  @dev            See {getBaseURICount}.
                             *  @param _index   Index of the desired batch in batchIds array.
                             */
                            function getBatchIdAtIndex(uint256 _index) public view returns (uint256) {
                                if (_index >= getBaseURICount()) {
                                    revert BatchMintInvalidBatchId(_index);
                                }
                                return batchIds[_index];
                            }
                            /// @dev Returns the id for the batch of tokens the given tokenId belongs to.
                            function _getBatchId(uint256 _tokenId) internal view returns (uint256 batchId, uint256 index) {
                                uint256 numOfTokenBatches = getBaseURICount();
                                uint256[] memory indices = batchIds;
                                for (uint256 i = 0; i < numOfTokenBatches; i += 1) {
                                    if (_tokenId < indices[i]) {
                                        index = i;
                                        batchId = indices[i];
                                        return (batchId, index);
                                    }
                                }
                                revert BatchMintInvalidTokenId(_tokenId);
                            }
                            /// @dev Returns the baseURI for a token. The intended metadata URI for the token is baseURI + tokenId.
                            function _getBaseURI(uint256 _tokenId) internal view returns (string memory) {
                                uint256 numOfTokenBatches = getBaseURICount();
                                uint256[] memory indices = batchIds;
                                for (uint256 i = 0; i < numOfTokenBatches; i += 1) {
                                    if (_tokenId < indices[i]) {
                                        return baseURI[indices[i]];
                                    }
                                }
                                revert BatchMintInvalidTokenId(_tokenId);
                            }
                            /// @dev returns the starting tokenId of a given batchId.
                            function _getBatchStartId(uint256 _batchID) internal view returns (uint256) {
                                uint256 numOfTokenBatches = getBaseURICount();
                                uint256[] memory indices = batchIds;
                                for (uint256 i = 0; i < numOfTokenBatches; i++) {
                                    if (_batchID == indices[i]) {
                                        if (i > 0) {
                                            return indices[i - 1];
                                        }
                                        return 0;
                                    }
                                }
                                revert BatchMintInvalidBatchId(_batchID);
                            }
                            /// @dev Sets the base URI for the batch of tokens with the given batchId.
                            function _setBaseURI(uint256 _batchId, string memory _baseURI) internal {
                                if (batchFrozen[_batchId]) {
                                    revert BatchMintMetadataFrozen(_batchId);
                                }
                                baseURI[_batchId] = _baseURI;
                                emit BatchMetadataUpdate(_getBatchStartId(_batchId), _batchId);
                            }
                            /// @dev Freezes the base URI for the batch of tokens with the given batchId.
                            function _freezeBaseURI(uint256 _batchId) internal {
                                string memory baseURIForBatch = baseURI[_batchId];
                                if (bytes(baseURIForBatch).length == 0) {
                                    revert BatchMintInvalidBatchId(_batchId);
                                }
                                batchFrozen[_batchId] = true;
                                emit MetadataFrozen();
                            }
                            /// @dev Mints a batch of tokenIds and associates a common baseURI to all those Ids.
                            function _batchMintMetadata(
                                uint256 _startId,
                                uint256 _amountToMint,
                                string memory _baseURIForTokens
                            ) internal returns (uint256 nextTokenIdToMint, uint256 batchId) {
                                batchId = _startId + _amountToMint;
                                nextTokenIdToMint = batchId;
                                batchIds.push(batchId);
                                baseURI[batchId] = _baseURIForTokens;
                            }
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./interface/IContractMetadata.sol";
                        /**
                         *  @title   Contract Metadata
                         *  @notice  Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
                         *           for you contract.
                         *           Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
                         */
                        abstract contract ContractMetadata is IContractMetadata {
                            /// @dev The sender is not authorized to perform the action
                            error ContractMetadataUnauthorized();
                            /// @notice Returns the contract metadata URI.
                            string public override contractURI;
                            /**
                             *  @notice         Lets a contract admin set the URI for contract-level metadata.
                             *  @dev            Caller should be authorized to setup contractURI, e.g. contract admin.
                             *                  See {_canSetContractURI}.
                             *                  Emits {ContractURIUpdated Event}.
                             *
                             *  @param _uri     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                             */
                            function setContractURI(string memory _uri) external override {
                                if (!_canSetContractURI()) {
                                    revert ContractMetadataUnauthorized();
                                }
                                _setupContractURI(_uri);
                            }
                            /// @dev Lets a contract admin set the URI for contract-level metadata.
                            function _setupContractURI(string memory _uri) internal {
                                string memory prevURI = contractURI;
                                contractURI = _uri;
                                emit ContractURIUpdated(prevURI, _uri);
                            }
                            /// @dev Returns whether contract metadata can be set in the given execution context.
                            function _canSetContractURI() internal view virtual returns (bool);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./interface/IDelayedReveal.sol";
                        /**
                         *  @title   Delayed Reveal
                         *  @notice  Thirdweb's `DelayedReveal` is a contract extension for base NFT contracts. It lets you create batches of
                         *           'delayed-reveal' NFTs. You can learn more about the usage of delayed reveal NFTs here - https://blog.thirdweb.com/delayed-reveal-nfts
                         */
                        abstract contract DelayedReveal is IDelayedReveal {
                            /// @dev The contract doesn't have any url to be delayed revealed
                            error DelayedRevealNothingToReveal();
                            /// @dev The result of the returned an incorrect hash
                            error DelayedRevealIncorrectResultHash(bytes32 expected, bytes32 actual);
                            /// @dev Mapping from tokenId of a batch of tokens => to delayed reveal data.
                            mapping(uint256 => bytes) public encryptedData;
                            /// @dev Sets the delayed reveal data for a batchId.
                            function _setEncryptedData(uint256 _batchId, bytes memory _encryptedData) internal {
                                encryptedData[_batchId] = _encryptedData;
                            }
                            /**
                             *  @notice             Returns revealed URI for a batch of NFTs.
                             *  @dev                Reveal encrypted base URI for `_batchId` with caller/admin's `_key` used for encryption.
                             *                      Reverts if there's no encrypted URI for `_batchId`.
                             *                      See {encryptDecrypt}.
                             *
                             *  @param _batchId     ID of the batch for which URI is being revealed.
                             *  @param _key         Secure key used by caller/admin for encryption of baseURI.
                             *
                             *  @return revealedURI Decrypted base URI.
                             */
                            function getRevealURI(uint256 _batchId, bytes calldata _key) public view returns (string memory revealedURI) {
                                bytes memory data = encryptedData[_batchId];
                                if (data.length == 0) {
                                    revert DelayedRevealNothingToReveal();
                                }
                                (bytes memory encryptedURI, bytes32 provenanceHash) = abi.decode(data, (bytes, bytes32));
                                revealedURI = string(encryptDecrypt(encryptedURI, _key));
                                if (keccak256(abi.encodePacked(revealedURI, _key, block.chainid)) != provenanceHash) {
                                    revert DelayedRevealIncorrectResultHash(
                                        provenanceHash,
                                        keccak256(abi.encodePacked(revealedURI, _key, block.chainid))
                                    );
                                }
                            }
                            /**
                             *  @notice         Encrypt/decrypt data on chain.
                             *  @dev            Encrypt/decrypt given `data` with `key`. Uses inline assembly.
                             *                  See: https://ethereum.stackexchange.com/questions/69825/decrypt-message-on-chain
                             *
                             *  @param data     Bytes of data to encrypt/decrypt.
                             *  @param key      Secure key used by caller for encryption/decryption.
                             *
                             *  @return result  Output after encryption/decryption of given data.
                             */
                            function encryptDecrypt(bytes memory data, bytes calldata key) public pure override returns (bytes memory result) {
                                // Store data length on stack for later use
                                uint256 length = data.length;
                                // solhint-disable-next-line no-inline-assembly
                                assembly {
                                    // Set result to free memory pointer
                                    result := mload(0x40)
                                    // Increase free memory pointer by lenght + 32
                                    mstore(0x40, add(add(result, length), 32))
                                    // Set result length
                                    mstore(result, length)
                                }
                                // Iterate over the data stepping by 32 bytes
                                for (uint256 i = 0; i < length; i += 32) {
                                    // Generate hash of the key and offset
                                    bytes32 hash = keccak256(abi.encodePacked(key, i));
                                    bytes32 chunk;
                                    // solhint-disable-next-line no-inline-assembly
                                    assembly {
                                        // Read 32-bytes data chunk
                                        chunk := mload(add(data, add(i, 32)))
                                    }
                                    // XOR the chunk with hash
                                    chunk ^= hash;
                                    // solhint-disable-next-line no-inline-assembly
                                    assembly {
                                        // Write 32-byte encrypted chunk
                                        mstore(add(result, add(i, 32)), chunk)
                                    }
                                }
                            }
                            /**
                             *  @notice         Returns whether the relvant batch of NFTs is subject to a delayed reveal.
                             *  @dev            Returns `true` if `_batchId`'s base URI is encrypted.
                             *  @param _batchId ID of a batch of NFTs.
                             */
                            function isEncryptedBatch(uint256 _batchId) public view returns (bool) {
                                return encryptedData[_batchId].length > 0;
                            }
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./interface/IDrop.sol";
                        import "../lib/MerkleProof.sol";
                        abstract contract Drop is IDrop {
                            /// @dev The sender is not authorized to perform the action
                            error DropUnauthorized();
                            /// @dev Exceeded the max token total supply
                            error DropExceedMaxSupply();
                            /// @dev No active claim condition
                            error DropNoActiveCondition();
                            /// @dev Claim condition invalid currency or price
                            error DropClaimInvalidTokenPrice(
                                address expectedCurrency,
                                uint256 expectedPricePerToken,
                                address actualCurrency,
                                uint256 actualExpectedPricePerToken
                            );
                            /// @dev Claim condition exceeded limit
                            error DropClaimExceedLimit(uint256 expected, uint256 actual);
                            /// @dev Claim condition exceeded max supply
                            error DropClaimExceedMaxSupply(uint256 expected, uint256 actual);
                            /// @dev Claim condition not started yet
                            error DropClaimNotStarted(uint256 expected, uint256 actual);
                            /*///////////////////////////////////////////////////////////////
                                                    State variables
                            //////////////////////////////////////////////////////////////*/
                            /// @dev The active conditions for claiming tokens.
                            ClaimConditionList public claimCondition;
                            /*///////////////////////////////////////////////////////////////
                                                    Drop logic
                            //////////////////////////////////////////////////////////////*/
                            /// @dev Lets an account claim tokens.
                            function claim(
                                address _receiver,
                                uint256 _quantity,
                                address _currency,
                                uint256 _pricePerToken,
                                AllowlistProof calldata _allowlistProof,
                                bytes memory _data
                            ) public payable virtual override {
                                _beforeClaim(_receiver, _quantity, _currency, _pricePerToken, _allowlistProof, _data);
                                uint256 activeConditionId = getActiveClaimConditionId();
                                verifyClaim(activeConditionId, _dropMsgSender(), _quantity, _currency, _pricePerToken, _allowlistProof);
                                // Update contract state.
                                claimCondition.conditions[activeConditionId].supplyClaimed += _quantity;
                                claimCondition.supplyClaimedByWallet[activeConditionId][_dropMsgSender()] += _quantity;
                                // If there's a price, collect price.
                                _collectPriceOnClaim(address(0), _quantity, _currency, _pricePerToken);
                                // Mint the relevant tokens to claimer.
                                uint256 startTokenId = _transferTokensOnClaim(_receiver, _quantity);
                                emit TokensClaimed(activeConditionId, _dropMsgSender(), _receiver, startTokenId, _quantity);
                                _afterClaim(_receiver, _quantity, _currency, _pricePerToken, _allowlistProof, _data);
                            }
                            /// @dev Lets a contract admin set claim conditions.
                            function setClaimConditions(
                                ClaimCondition[] calldata _conditions,
                                bool _resetClaimEligibility
                            ) external virtual override {
                                if (!_canSetClaimConditions()) {
                                    revert DropUnauthorized();
                                }
                                uint256 existingStartIndex = claimCondition.currentStartId;
                                uint256 existingPhaseCount = claimCondition.count;
                                /**
                                 *  The mapping `supplyClaimedByWallet` uses a claim condition's UID as a key.
                                 *
                                 *  If `_resetClaimEligibility == true`, we assign completely new UIDs to the claim
                                 *  conditions in `_conditions`, effectively resetting the restrictions on claims expressed
                                 *  by `supplyClaimedByWallet`.
                                 */
                                uint256 newStartIndex = existingStartIndex;
                                if (_resetClaimEligibility) {
                                    newStartIndex = existingStartIndex + existingPhaseCount;
                                }
                                claimCondition.count = _conditions.length;
                                claimCondition.currentStartId = newStartIndex;
                                uint256 lastConditionStartTimestamp;
                                for (uint256 i = 0; i < _conditions.length; i++) {
                                    require(i == 0 || lastConditionStartTimestamp < _conditions[i].startTimestamp, "ST");
                                    uint256 supplyClaimedAlready = claimCondition.conditions[newStartIndex + i].supplyClaimed;
                                    if (supplyClaimedAlready > _conditions[i].maxClaimableSupply) {
                                        revert DropExceedMaxSupply();
                                    }
                                    claimCondition.conditions[newStartIndex + i] = _conditions[i];
                                    claimCondition.conditions[newStartIndex + i].supplyClaimed = supplyClaimedAlready;
                                    lastConditionStartTimestamp = _conditions[i].startTimestamp;
                                }
                                /**
                                 *  Gas refunds (as much as possible)
                                 *
                                 *  If `_resetClaimEligibility == true`, we assign completely new UIDs to the claim
                                 *  conditions in `_conditions`. So, we delete claim conditions with UID < `newStartIndex`.
                                 *
                                 *  If `_resetClaimEligibility == false`, and there are more existing claim conditions
                                 *  than in `_conditions`, we delete the existing claim conditions that don't get replaced
                                 *  by the conditions in `_conditions`.
                                 */
                                if (_resetClaimEligibility) {
                                    for (uint256 i = existingStartIndex; i < newStartIndex; i++) {
                                        delete claimCondition.conditions[i];
                                    }
                                } else {
                                    if (existingPhaseCount > _conditions.length) {
                                        for (uint256 i = _conditions.length; i < existingPhaseCount; i++) {
                                            delete claimCondition.conditions[newStartIndex + i];
                                        }
                                    }
                                }
                                emit ClaimConditionsUpdated(_conditions, _resetClaimEligibility);
                            }
                            /// @dev Checks a request to claim NFTs against the active claim condition's criteria.
                            function verifyClaim(
                                uint256 _conditionId,
                                address _claimer,
                                uint256 _quantity,
                                address _currency,
                                uint256 _pricePerToken,
                                AllowlistProof calldata _allowlistProof
                            ) public view virtual returns (bool isOverride) {
                                ClaimCondition memory currentClaimPhase = claimCondition.conditions[_conditionId];
                                uint256 claimLimit = currentClaimPhase.quantityLimitPerWallet;
                                uint256 claimPrice = currentClaimPhase.pricePerToken;
                                address claimCurrency = currentClaimPhase.currency;
                                /*
                                 * Here `isOverride` implies that if the merkle proof verification fails,
                                 * the claimer would claim through open claim limit instead of allowlisted limit.
                                 */
                                if (currentClaimPhase.merkleRoot != bytes32(0)) {
                                    (isOverride, ) = MerkleProof.verify(
                                        _allowlistProof.proof,
                                        currentClaimPhase.merkleRoot,
                                        keccak256(
                                            abi.encodePacked(
                                                _claimer,
                                                _allowlistProof.quantityLimitPerWallet,
                                                _allowlistProof.pricePerToken,
                                                _allowlistProof.currency
                                            )
                                        )
                                    );
                                }
                                if (isOverride) {
                                    claimLimit = _allowlistProof.quantityLimitPerWallet != 0
                                        ? _allowlistProof.quantityLimitPerWallet
                                        : claimLimit;
                                    claimPrice = _allowlistProof.pricePerToken != type(uint256).max
                                        ? _allowlistProof.pricePerToken
                                        : claimPrice;
                                    claimCurrency = _allowlistProof.pricePerToken != type(uint256).max && _allowlistProof.currency != address(0)
                                        ? _allowlistProof.currency
                                        : claimCurrency;
                                }
                                uint256 supplyClaimedByWallet = claimCondition.supplyClaimedByWallet[_conditionId][_claimer];
                                if (_currency != claimCurrency || _pricePerToken != claimPrice) {
                                    revert DropClaimInvalidTokenPrice(_currency, _pricePerToken, claimCurrency, claimPrice);
                                }
                                if (_quantity == 0 || (_quantity + supplyClaimedByWallet > claimLimit)) {
                                    revert DropClaimExceedLimit(claimLimit, _quantity + supplyClaimedByWallet);
                                }
                                if (currentClaimPhase.supplyClaimed + _quantity > currentClaimPhase.maxClaimableSupply) {
                                    revert DropClaimExceedMaxSupply(
                                        currentClaimPhase.maxClaimableSupply,
                                        currentClaimPhase.supplyClaimed + _quantity
                                    );
                                }
                                if (currentClaimPhase.startTimestamp > block.timestamp) {
                                    revert DropClaimNotStarted(currentClaimPhase.startTimestamp, block.timestamp);
                                }
                            }
                            /// @dev At any given moment, returns the uid for the active claim condition.
                            function getActiveClaimConditionId() public view returns (uint256) {
                                for (uint256 i = claimCondition.currentStartId + claimCondition.count; i > claimCondition.currentStartId; i--) {
                                    if (block.timestamp >= claimCondition.conditions[i - 1].startTimestamp) {
                                        return i - 1;
                                    }
                                }
                                revert DropNoActiveCondition();
                            }
                            /// @dev Returns the claim condition at the given uid.
                            function getClaimConditionById(uint256 _conditionId) external view returns (ClaimCondition memory condition) {
                                condition = claimCondition.conditions[_conditionId];
                            }
                            /// @dev Returns the supply claimed by claimer for a given conditionId.
                            function getSupplyClaimedByWallet(
                                uint256 _conditionId,
                                address _claimer
                            ) public view returns (uint256 supplyClaimedByWallet) {
                                supplyClaimedByWallet = claimCondition.supplyClaimedByWallet[_conditionId][_claimer];
                            }
                            /*////////////////////////////////////////////////////////////////////
                                Optional hooks that can be implemented in the derived contract
                            ///////////////////////////////////////////////////////////////////*/
                            /// @dev Exposes the ability to override the msg sender.
                            function _dropMsgSender() internal virtual returns (address) {
                                return msg.sender;
                            }
                            /// @dev Runs before every `claim` function call.
                            function _beforeClaim(
                                address _receiver,
                                uint256 _quantity,
                                address _currency,
                                uint256 _pricePerToken,
                                AllowlistProof calldata _allowlistProof,
                                bytes memory _data
                            ) internal virtual {}
                            /// @dev Runs after every `claim` function call.
                            function _afterClaim(
                                address _receiver,
                                uint256 _quantity,
                                address _currency,
                                uint256 _pricePerToken,
                                AllowlistProof calldata _allowlistProof,
                                bytes memory _data
                            ) internal virtual {}
                            /*///////////////////////////////////////////////////////////////
                                Virtual functions: to be implemented in derived contract
                            //////////////////////////////////////////////////////////////*/
                            /// @dev Collects and distributes the primary sale value of NFTs being claimed.
                            function _collectPriceOnClaim(
                                address _primarySaleRecipient,
                                uint256 _quantityToClaim,
                                address _currency,
                                uint256 _pricePerToken
                            ) internal virtual;
                            /// @dev Transfers the NFTs being claimed.
                            function _transferTokensOnClaim(
                                address _to,
                                uint256 _quantityBeingClaimed
                            ) internal virtual returns (uint256 startTokenId);
                            /// @dev Determine what wallet can update claim conditions
                            function _canSetClaimConditions() internal view virtual returns (bool);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./interface/ILazyMint.sol";
                        import "./BatchMintMetadata.sol";
                        /**
                         *  The `LazyMint` is a contract extension for any base NFT contract. It lets you 'lazy mint' any number of NFTs
                         *  at once. Here, 'lazy mint' means defining the metadata for particular tokenIds of your NFT contract, without actually
                         *  minting a non-zero balance of NFTs of those tokenIds.
                         */
                        abstract contract LazyMint is ILazyMint, BatchMintMetadata {
                            /// @dev The sender is not authorized to perform the action
                            error LazyMintUnauthorized();
                            error LazyMintInvalidAmount();
                            /// @notice The tokenId assigned to the next new NFT to be lazy minted.
                            uint256 internal nextTokenIdToLazyMint;
                            /**
                             *  @notice                  Lets an authorized address lazy mint a given amount of NFTs.
                             *
                             *  @param _amount           The number of NFTs to lazy mint.
                             *  @param _baseURIForTokens The base URI for the 'n' number of NFTs being lazy minted, where the metadata for each
                             *                           of those NFTs is `${baseURIForTokens}/${tokenId}`.
                             *  @param _data             Additional bytes data to be used at the discretion of the consumer of the contract.
                             *  @return batchId          A unique integer identifier for the batch of NFTs lazy minted together.
                             */
                            function lazyMint(
                                uint256 _amount,
                                string calldata _baseURIForTokens,
                                bytes calldata _data
                            ) public virtual override returns (uint256 batchId) {
                                if (!_canLazyMint()) {
                                    revert LazyMintUnauthorized();
                                }
                                if (_amount == 0) {
                                    revert LazyMintInvalidAmount();
                                }
                                uint256 startId = nextTokenIdToLazyMint;
                                (nextTokenIdToLazyMint, batchId) = _batchMintMetadata(startId, _amount, _baseURIForTokens);
                                emit TokensLazyMinted(startId, startId + _amount - 1, _baseURIForTokens, _data);
                                return batchId;
                            }
                            /// @dev Returns whether lazy minting can be performed in the given execution context.
                            function _canLazyMint() internal view virtual returns (bool);
                        }
                        // SPDX-License-Identifier: Apache 2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "../lib/Address.sol";
                        import "./interface/IMulticall.sol";
                        /**
                         * @dev Provides a function to batch together multiple calls in a single external call.
                         *
                         * _Available since v4.1._
                         */
                        contract Multicall is IMulticall {
                            /**
                             *  @notice Receives and executes a batch of function calls on this contract.
                             *  @dev Receives and executes a batch of function calls on this contract.
                             *
                             *  @param data The bytes data that makes up the batch of function calls to execute.
                             *  @return results The bytes data that makes up the result of the batch of function calls executed.
                             */
                            function multicall(bytes[] calldata data) external returns (bytes[] memory results) {
                                results = new bytes[](data.length);
                                address sender = _msgSender();
                                bool isForwarder = msg.sender != sender;
                                for (uint256 i = 0; i < data.length; i++) {
                                    if (isForwarder) {
                                        results[i] = Address.functionDelegateCall(address(this), abi.encodePacked(data[i], sender));
                                    } else {
                                        results[i] = Address.functionDelegateCall(address(this), data[i]);
                                    }
                                }
                                return results;
                            }
                            /// @notice Returns the sender in the given execution context.
                            function _msgSender() internal view virtual returns (address) {
                                return msg.sender;
                            }
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./interface/IOwnable.sol";
                        /**
                         *  @title   Ownable
                         *  @notice  Thirdweb's `Ownable` is a contract extension to be used with any base contract. It exposes functions for setting and reading
                         *           who the 'owner' of the inheriting smart contract is, and lets the inheriting contract perform conditional logic that uses
                         *           information about who the contract's owner is.
                         */
                        abstract contract Ownable is IOwnable {
                            /// @dev The sender is not authorized to perform the action
                            error OwnableUnauthorized();
                            /// @dev Owner of the contract (purpose: OpenSea compatibility)
                            address private _owner;
                            /// @dev Reverts if caller is not the owner.
                            modifier onlyOwner() {
                                if (msg.sender != _owner) {
                                    revert OwnableUnauthorized();
                                }
                                _;
                            }
                            /**
                             *  @notice Returns the owner of the contract.
                             */
                            function owner() public view override returns (address) {
                                return _owner;
                            }
                            /**
                             *  @notice Lets an authorized wallet set a new owner for the contract.
                             *  @param _newOwner The address to set as the new owner of the contract.
                             */
                            function setOwner(address _newOwner) external override {
                                if (!_canSetOwner()) {
                                    revert OwnableUnauthorized();
                                }
                                _setupOwner(_newOwner);
                            }
                            /// @dev Lets a contract admin set a new owner for the contract. The new owner must be a contract admin.
                            function _setupOwner(address _newOwner) internal {
                                address _prevOwner = _owner;
                                _owner = _newOwner;
                                emit OwnerUpdated(_prevOwner, _newOwner);
                            }
                            /// @dev Returns whether owner can be set in the given execution context.
                            function _canSetOwner() internal view virtual returns (bool);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./interface/IPermissions.sol";
                        import "../lib/Strings.sol";
                        /**
                         *  @title   Permissions
                         *  @dev     This contracts provides extending-contracts with role-based access control mechanisms
                         */
                        contract Permissions is IPermissions {
                            /// @dev The `account` is missing a role.
                            error PermissionsUnauthorizedAccount(address account, bytes32 neededRole);
                            /// @dev The `account` already is a holder of `role`
                            error PermissionsAlreadyGranted(address account, bytes32 role);
                            /// @dev Invalid priviledge to revoke
                            error PermissionsInvalidPermission(address expected, address actual);
                            /// @dev Map from keccak256 hash of a role => a map from address => whether address has role.
                            mapping(bytes32 => mapping(address => bool)) private _hasRole;
                            /// @dev Map from keccak256 hash of a role to role admin. See {getRoleAdmin}.
                            mapping(bytes32 => bytes32) private _getRoleAdmin;
                            /// @dev Default admin role for all roles. Only accounts with this role can grant/revoke other roles.
                            bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
                            /// @dev Modifier that checks if an account has the specified role; reverts otherwise.
                            modifier onlyRole(bytes32 role) {
                                _checkRole(role, msg.sender);
                                _;
                            }
                            /**
                             *  @notice         Checks whether an account has a particular role.
                             *  @dev            Returns `true` if `account` has been granted `role`.
                             *
                             *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                             *  @param account  Address of the account for which the role is being checked.
                             */
                            function hasRole(bytes32 role, address account) public view override returns (bool) {
                                return _hasRole[role][account];
                            }
                            /**
                             *  @notice         Checks whether an account has a particular role;
                             *                  role restrictions can be swtiched on and off.
                             *
                             *  @dev            Returns `true` if `account` has been granted `role`.
                             *                  Role restrictions can be swtiched on and off:
                             *                      - If address(0) has ROLE, then the ROLE restrictions
                             *                        don't apply.
                             *                      - If address(0) does not have ROLE, then the ROLE
                             *                        restrictions will apply.
                             *
                             *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                             *  @param account  Address of the account for which the role is being checked.
                             */
                            function hasRoleWithSwitch(bytes32 role, address account) public view returns (bool) {
                                if (!_hasRole[role][address(0)]) {
                                    return _hasRole[role][account];
                                }
                                return true;
                            }
                            /**
                             *  @notice         Returns the admin role that controls the specified role.
                             *  @dev            See {grantRole} and {revokeRole}.
                             *                  To change a role's admin, use {_setRoleAdmin}.
                             *
                             *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                             */
                            function getRoleAdmin(bytes32 role) external view override returns (bytes32) {
                                return _getRoleAdmin[role];
                            }
                            /**
                             *  @notice         Grants a role to an account, if not previously granted.
                             *  @dev            Caller must have admin role for the `role`.
                             *                  Emits {RoleGranted Event}.
                             *
                             *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                             *  @param account  Address of the account to which the role is being granted.
                             */
                            function grantRole(bytes32 role, address account) public virtual override {
                                _checkRole(_getRoleAdmin[role], msg.sender);
                                if (_hasRole[role][account]) {
                                    revert PermissionsAlreadyGranted(account, role);
                                }
                                _setupRole(role, account);
                            }
                            /**
                             *  @notice         Revokes role from an account.
                             *  @dev            Caller must have admin role for the `role`.
                             *                  Emits {RoleRevoked Event}.
                             *
                             *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                             *  @param account  Address of the account from which the role is being revoked.
                             */
                            function revokeRole(bytes32 role, address account) public virtual override {
                                _checkRole(_getRoleAdmin[role], msg.sender);
                                _revokeRole(role, account);
                            }
                            /**
                             *  @notice         Revokes role from the account.
                             *  @dev            Caller must have the `role`, with caller being the same as `account`.
                             *                  Emits {RoleRevoked Event}.
                             *
                             *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                             *  @param account  Address of the account from which the role is being revoked.
                             */
                            function renounceRole(bytes32 role, address account) public virtual override {
                                if (msg.sender != account) {
                                    revert PermissionsInvalidPermission(msg.sender, account);
                                }
                                _revokeRole(role, account);
                            }
                            /// @dev Sets `adminRole` as `role`'s admin role.
                            function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                                bytes32 previousAdminRole = _getRoleAdmin[role];
                                _getRoleAdmin[role] = adminRole;
                                emit RoleAdminChanged(role, previousAdminRole, adminRole);
                            }
                            /// @dev Sets up `role` for `account`
                            function _setupRole(bytes32 role, address account) internal virtual {
                                _hasRole[role][account] = true;
                                emit RoleGranted(role, account, msg.sender);
                            }
                            /// @dev Revokes `role` from `account`
                            function _revokeRole(bytes32 role, address account) internal virtual {
                                _checkRole(role, account);
                                delete _hasRole[role][account];
                                emit RoleRevoked(role, account, msg.sender);
                            }
                            /// @dev Checks `role` for `account`. Reverts with a message including the required role.
                            function _checkRole(bytes32 role, address account) internal view virtual {
                                if (!_hasRole[role][account]) {
                                    revert PermissionsUnauthorizedAccount(account, role);
                                }
                            }
                            /// @dev Checks `role` for `account`. Reverts with a message including the required role.
                            function _checkRoleWithSwitch(bytes32 role, address account) internal view virtual {
                                if (!hasRoleWithSwitch(role, account)) {
                                    revert PermissionsUnauthorizedAccount(account, role);
                                }
                            }
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./interface/IPermissionsEnumerable.sol";
                        import "./Permissions.sol";
                        /**
                         *  @title   PermissionsEnumerable
                         *  @dev     This contracts provides extending-contracts with role-based access control mechanisms.
                         *           Also provides interfaces to view all members with a given role, and total count of members.
                         */
                        contract PermissionsEnumerable is IPermissionsEnumerable, Permissions {
                            /**
                             *  @notice A data structure to store data of members for a given role.
                             *
                             *  @param index    Current index in the list of accounts that have a role.
                             *  @param members  map from index => address of account that has a role
                             *  @param indexOf  map from address => index which the account has.
                             */
                            struct RoleMembers {
                                uint256 index;
                                mapping(uint256 => address) members;
                                mapping(address => uint256) indexOf;
                            }
                            /// @dev map from keccak256 hash of a role to its members' data. See {RoleMembers}.
                            mapping(bytes32 => RoleMembers) private roleMembers;
                            /**
                             *  @notice         Returns the role-member from a list of members for a role,
                             *                  at a given index.
                             *  @dev            Returns `member` who has `role`, at `index` of role-members list.
                             *                  See struct {RoleMembers}, and mapping {roleMembers}
                             *
                             *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                             *  @param index    Index in list of current members for the role.
                             *
                             *  @return member  Address of account that has `role`
                             */
                            function getRoleMember(bytes32 role, uint256 index) external view override returns (address member) {
                                uint256 currentIndex = roleMembers[role].index;
                                uint256 check;
                                for (uint256 i = 0; i < currentIndex; i += 1) {
                                    if (roleMembers[role].members[i] != address(0)) {
                                        if (check == index) {
                                            member = roleMembers[role].members[i];
                                            return member;
                                        }
                                        check += 1;
                                    } else if (hasRole(role, address(0)) && i == roleMembers[role].indexOf[address(0)]) {
                                        check += 1;
                                    }
                                }
                            }
                            /**
                             *  @notice         Returns total number of accounts that have a role.
                             *  @dev            Returns `count` of accounts that have `role`.
                             *                  See struct {RoleMembers}, and mapping {roleMembers}
                             *
                             *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                             *
                             *  @return count   Total number of accounts that have `role`
                             */
                            function getRoleMemberCount(bytes32 role) external view override returns (uint256 count) {
                                uint256 currentIndex = roleMembers[role].index;
                                for (uint256 i = 0; i < currentIndex; i += 1) {
                                    if (roleMembers[role].members[i] != address(0)) {
                                        count += 1;
                                    }
                                }
                                if (hasRole(role, address(0))) {
                                    count += 1;
                                }
                            }
                            /// @dev Revokes `role` from `account`, and removes `account` from {roleMembers}
                            ///      See {_removeMember}
                            function _revokeRole(bytes32 role, address account) internal override {
                                super._revokeRole(role, account);
                                _removeMember(role, account);
                            }
                            /// @dev Grants `role` to `account`, and adds `account` to {roleMembers}
                            ///      See {_addMember}
                            function _setupRole(bytes32 role, address account) internal override {
                                super._setupRole(role, account);
                                _addMember(role, account);
                            }
                            /// @dev adds `account` to {roleMembers}, for `role`
                            function _addMember(bytes32 role, address account) internal {
                                uint256 idx = roleMembers[role].index;
                                roleMembers[role].index += 1;
                                roleMembers[role].members[idx] = account;
                                roleMembers[role].indexOf[account] = idx;
                            }
                            /// @dev removes `account` from {roleMembers}, for `role`
                            function _removeMember(bytes32 role, address account) internal {
                                uint256 idx = roleMembers[role].indexOf[account];
                                delete roleMembers[role].members[idx];
                                delete roleMembers[role].indexOf[account];
                            }
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./interface/IPlatformFee.sol";
                        /**
                         *  @title   Platform Fee
                         *  @notice  Thirdweb's `PlatformFee` is a contract extension to be used with any base contract. It exposes functions for setting and reading
                         *           the recipient of platform fee and the platform fee basis points, and lets the inheriting contract perform conditional logic
                         *           that uses information about platform fees, if desired.
                         */
                        abstract contract PlatformFee is IPlatformFee {
                            /// @dev The sender is not authorized to perform the action
                            error PlatformFeeUnauthorized();
                            /// @dev The recipient is invalid
                            error PlatformFeeInvalidRecipient(address recipient);
                            /// @dev The fee bps exceeded the max value
                            error PlatformFeeExceededMaxFeeBps(uint256 max, uint256 actual);
                            /// @dev The address that receives all platform fees from all sales.
                            address private platformFeeRecipient;
                            /// @dev The % of primary sales collected as platform fees.
                            uint16 private platformFeeBps;
                            /// @dev Fee type variants: percentage fee and flat fee
                            PlatformFeeType private platformFeeType;
                            /// @dev The flat amount collected by the contract as fees on primary sales.
                            uint256 private flatPlatformFee;
                            /// @dev Returns the platform fee recipient and bps.
                            function getPlatformFeeInfo() public view override returns (address, uint16) {
                                return (platformFeeRecipient, uint16(platformFeeBps));
                            }
                            /// @dev Returns the platform fee bps and recipient.
                            function getFlatPlatformFeeInfo() public view returns (address, uint256) {
                                return (platformFeeRecipient, flatPlatformFee);
                            }
                            /// @dev Returns the platform fee type.
                            function getPlatformFeeType() public view returns (PlatformFeeType) {
                                return platformFeeType;
                            }
                            /**
                             *  @notice         Updates the platform fee recipient and bps.
                             *  @dev            Caller should be authorized to set platform fee info.
                             *                  See {_canSetPlatformFeeInfo}.
                             *                  Emits {PlatformFeeInfoUpdated Event}; See {_setupPlatformFeeInfo}.
                             *
                             *  @param _platformFeeRecipient   Address to be set as new platformFeeRecipient.
                             *  @param _platformFeeBps         Updated platformFeeBps.
                             */
                            function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external override {
                                if (!_canSetPlatformFeeInfo()) {
                                    revert PlatformFeeUnauthorized();
                                }
                                _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps);
                            }
                            /// @dev Sets the platform fee recipient and bps
                            function _setupPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) internal {
                                if (_platformFeeBps > 10_000) {
                                    revert PlatformFeeExceededMaxFeeBps(10_000, _platformFeeBps);
                                }
                                if (_platformFeeRecipient == address(0)) {
                                    revert PlatformFeeInvalidRecipient(_platformFeeRecipient);
                                }
                                platformFeeBps = uint16(_platformFeeBps);
                                platformFeeRecipient = _platformFeeRecipient;
                                emit PlatformFeeInfoUpdated(_platformFeeRecipient, _platformFeeBps);
                            }
                            /// @notice Lets a module admin set a flat fee on primary sales.
                            function setFlatPlatformFeeInfo(address _platformFeeRecipient, uint256 _flatFee) external {
                                if (!_canSetPlatformFeeInfo()) {
                                    revert PlatformFeeUnauthorized();
                                }
                                _setupFlatPlatformFeeInfo(_platformFeeRecipient, _flatFee);
                            }
                            /// @dev Sets a flat fee on primary sales.
                            function _setupFlatPlatformFeeInfo(address _platformFeeRecipient, uint256 _flatFee) internal {
                                flatPlatformFee = _flatFee;
                                platformFeeRecipient = _platformFeeRecipient;
                                emit FlatPlatformFeeUpdated(_platformFeeRecipient, _flatFee);
                            }
                            /// @notice Lets a module admin set platform fee type.
                            function setPlatformFeeType(PlatformFeeType _feeType) external {
                                if (!_canSetPlatformFeeInfo()) {
                                    revert PlatformFeeUnauthorized();
                                }
                                _setupPlatformFeeType(_feeType);
                            }
                            /// @dev Sets platform fee type.
                            function _setupPlatformFeeType(PlatformFeeType _feeType) internal {
                                platformFeeType = _feeType;
                                emit PlatformFeeTypeUpdated(_feeType);
                            }
                            /// @dev Returns whether platform fee info can be set in the given execution context.
                            function _canSetPlatformFeeInfo() internal view virtual returns (bool);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./interface/IPrimarySale.sol";
                        /**
                         *  @title   Primary Sale
                         *  @notice  Thirdweb's `PrimarySale` is a contract extension to be used with any base contract. It exposes functions for setting and reading
                         *           the recipient of primary sales, and lets the inheriting contract perform conditional logic that uses information about
                         *           primary sales, if desired.
                         */
                        abstract contract PrimarySale is IPrimarySale {
                            /// @dev The sender is not authorized to perform the action
                            error PrimarySaleUnauthorized();
                            /// @dev The recipient is invalid
                            error PrimarySaleInvalidRecipient(address recipient);
                            /// @dev The address that receives all primary sales value.
                            address private recipient;
                            /// @dev Returns primary sale recipient address.
                            function primarySaleRecipient() public view override returns (address) {
                                return recipient;
                            }
                            /**
                             *  @notice         Updates primary sale recipient.
                             *  @dev            Caller should be authorized to set primary sales info.
                             *                  See {_canSetPrimarySaleRecipient}.
                             *                  Emits {PrimarySaleRecipientUpdated Event}; See {_setupPrimarySaleRecipient}.
                             *
                             *  @param _saleRecipient   Address to be set as new recipient of primary sales.
                             */
                            function setPrimarySaleRecipient(address _saleRecipient) external override {
                                if (!_canSetPrimarySaleRecipient()) {
                                    revert PrimarySaleUnauthorized();
                                }
                                _setupPrimarySaleRecipient(_saleRecipient);
                            }
                            /// @dev Lets a contract admin set the recipient for all primary sales.
                            function _setupPrimarySaleRecipient(address _saleRecipient) internal {
                                if (_saleRecipient == address(0)) {
                                    revert PrimarySaleInvalidRecipient(_saleRecipient);
                                }
                                recipient = _saleRecipient;
                                emit PrimarySaleRecipientUpdated(_saleRecipient);
                            }
                            /// @dev Returns whether primary sale recipient can be set in the given execution context.
                            function _canSetPrimarySaleRecipient() internal view virtual returns (bool);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./interface/IRoyalty.sol";
                        /**
                         *  @title   Royalty
                         *  @notice  Thirdweb's `Royalty` is a contract extension to be used with any base contract. It exposes functions for setting and reading
                         *           the recipient of royalty fee and the royalty fee basis points, and lets the inheriting contract perform conditional logic
                         *           that uses information about royalty fees, if desired.
                         *
                         *  @dev     The `Royalty` contract is ERC2981 compliant.
                         */
                        abstract contract Royalty is IRoyalty {
                            /// @dev The sender is not authorized to perform the action
                            error RoyaltyUnauthorized();
                            /// @dev The recipient is invalid
                            error RoyaltyInvalidRecipient(address recipient);
                            /// @dev The fee bps exceeded the max value
                            error RoyaltyExceededMaxFeeBps(uint256 max, uint256 actual);
                            /// @dev The (default) address that receives all royalty value.
                            address private royaltyRecipient;
                            /// @dev The (default) % of a sale to take as royalty (in basis points).
                            uint16 private royaltyBps;
                            /// @dev Token ID => royalty recipient and bps for token
                            mapping(uint256 => RoyaltyInfo) private royaltyInfoForToken;
                            /**
                             *  @notice   View royalty info for a given token and sale price.
                             *  @dev      Returns royalty amount and recipient for `tokenId` and `salePrice`.
                             *  @param tokenId          The tokenID of the NFT for which to query royalty info.
                             *  @param salePrice        Sale price of the token.
                             *
                             *  @return receiver        Address of royalty recipient account.
                             *  @return royaltyAmount   Royalty amount calculated at current royaltyBps value.
                             */
                            function royaltyInfo(
                                uint256 tokenId,
                                uint256 salePrice
                            ) external view virtual override returns (address receiver, uint256 royaltyAmount) {
                                (address recipient, uint256 bps) = getRoyaltyInfoForToken(tokenId);
                                receiver = recipient;
                                royaltyAmount = (salePrice * bps) / 10_000;
                            }
                            /**
                             *  @notice          View royalty info for a given token.
                             *  @dev             Returns royalty recipient and bps for `_tokenId`.
                             *  @param _tokenId  The tokenID of the NFT for which to query royalty info.
                             */
                            function getRoyaltyInfoForToken(uint256 _tokenId) public view override returns (address, uint16) {
                                RoyaltyInfo memory royaltyForToken = royaltyInfoForToken[_tokenId];
                                return
                                    royaltyForToken.recipient == address(0)
                                        ? (royaltyRecipient, uint16(royaltyBps))
                                        : (royaltyForToken.recipient, uint16(royaltyForToken.bps));
                            }
                            /**
                             *  @notice Returns the defualt royalty recipient and BPS for this contract's NFTs.
                             */
                            function getDefaultRoyaltyInfo() external view override returns (address, uint16) {
                                return (royaltyRecipient, uint16(royaltyBps));
                            }
                            /**
                             *  @notice         Updates default royalty recipient and bps.
                             *  @dev            Caller should be authorized to set royalty info.
                             *                  See {_canSetRoyaltyInfo}.
                             *                  Emits {DefaultRoyalty Event}; See {_setupDefaultRoyaltyInfo}.
                             *
                             *  @param _royaltyRecipient   Address to be set as default royalty recipient.
                             *  @param _royaltyBps         Updated royalty bps.
                             */
                            function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) external override {
                                if (!_canSetRoyaltyInfo()) {
                                    revert RoyaltyUnauthorized();
                                }
                                _setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps);
                            }
                            /// @dev Lets a contract admin update the default royalty recipient and bps.
                            function _setupDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) internal {
                                if (_royaltyBps > 10_000) {
                                    revert RoyaltyExceededMaxFeeBps(10_000, _royaltyBps);
                                }
                                royaltyRecipient = _royaltyRecipient;
                                royaltyBps = uint16(_royaltyBps);
                                emit DefaultRoyalty(_royaltyRecipient, _royaltyBps);
                            }
                            /**
                             *  @notice         Updates default royalty recipient and bps for a particular token.
                             *  @dev            Sets royalty info for `_tokenId`. Caller should be authorized to set royalty info.
                             *                  See {_canSetRoyaltyInfo}.
                             *                  Emits {RoyaltyForToken Event}; See {_setupRoyaltyInfoForToken}.
                             *
                             *  @param _recipient   Address to be set as royalty recipient for given token Id.
                             *  @param _bps         Updated royalty bps for the token Id.
                             */
                            function setRoyaltyInfoForToken(uint256 _tokenId, address _recipient, uint256 _bps) external override {
                                if (!_canSetRoyaltyInfo()) {
                                    revert RoyaltyUnauthorized();
                                }
                                _setupRoyaltyInfoForToken(_tokenId, _recipient, _bps);
                            }
                            /// @dev Lets a contract admin set the royalty recipient and bps for a particular token Id.
                            function _setupRoyaltyInfoForToken(uint256 _tokenId, address _recipient, uint256 _bps) internal {
                                if (_bps > 10_000) {
                                    revert RoyaltyExceededMaxFeeBps(10_000, _bps);
                                }
                                royaltyInfoForToken[_tokenId] = RoyaltyInfo({ recipient: _recipient, bps: _bps });
                                emit RoyaltyForToken(_tokenId, _recipient, _bps);
                            }
                            /// @dev Returns whether royalty info can be set in the given execution context.
                            function _canSetRoyaltyInfo() internal view virtual returns (bool);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        /**
                         *  The interface `IClaimCondition` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
                         *
                         *  A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
                         *  or added to by the contract admin. At any moment, there is only one active claim condition.
                         */
                        interface IClaimCondition {
                            /**
                             *  @notice The criteria that make up a claim condition.
                             *
                             *  @param startTimestamp                 The unix timestamp after which the claim condition applies.
                             *                                        The same claim condition applies until the `startTimestamp`
                             *                                        of the next claim condition.
                             *
                             *  @param maxClaimableSupply             The maximum total number of tokens that can be claimed under
                             *                                        the claim condition.
                             *
                             *  @param supplyClaimed                  At any given point, the number of tokens that have been claimed
                             *                                        under the claim condition.
                             *
                             *  @param quantityLimitPerWallet         The maximum number of tokens that can be claimed by a wallet.
                             *
                             *  @param merkleRoot                     The allowlist of addresses that can claim tokens under the claim
                             *                                        condition.
                             *
                             *  @param pricePerToken                  The price required to pay per token claimed.
                             *
                             *  @param currency                       The currency in which the `pricePerToken` must be paid.
                             *
                             *  @param metadata                       Claim condition metadata.
                             */
                            struct ClaimCondition {
                                uint256 startTimestamp;
                                uint256 maxClaimableSupply;
                                uint256 supplyClaimed;
                                uint256 quantityLimitPerWallet;
                                bytes32 merkleRoot;
                                uint256 pricePerToken;
                                address currency;
                                string metadata;
                            }
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./IClaimCondition.sol";
                        /**
                         *  The interface `IClaimConditionMultiPhase` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
                         *
                         *  An authorized wallet can set a series of claim conditions, ordered by their respective `startTimestamp`.
                         *  A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
                         *  or added to by the contract admin. At any moment, there is only one active claim condition.
                         */
                        interface IClaimConditionMultiPhase is IClaimCondition {
                            /**
                             *  @notice The set of all claim conditions, at any given moment.
                             *  Claim Phase ID = [currentStartId, currentStartId + length - 1];
                             *
                             *  @param currentStartId           The uid for the first claim condition amongst the current set of
                             *                                  claim conditions. The uid for each next claim condition is one
                             *                                  more than the previous claim condition's uid.
                             *
                             *  @param count                    The total number of phases / claim conditions in the list
                             *                                  of claim conditions.
                             *
                             *  @param conditions                   The claim conditions at a given uid. Claim conditions
                             *                                  are ordered in an ascending order by their `startTimestamp`.
                             *
                             *  @param supplyClaimedByWallet    Map from a claim condition uid and account to supply claimed by account.
                             */
                            struct ClaimConditionList {
                                uint256 currentStartId;
                                uint256 count;
                                mapping(uint256 => ClaimCondition) conditions;
                                mapping(uint256 => mapping(address => uint256)) supplyClaimedByWallet;
                            }
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        /**
                         *  Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
                         *  for you contract.
                         *
                         *  Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
                         */
                        interface IContractMetadata {
                            /// @dev Returns the metadata URI of the contract.
                            function contractURI() external view returns (string memory);
                            /**
                             *  @dev Sets contract URI for the storefront-level metadata of the contract.
                             *       Only module admin can call this function.
                             */
                            function setContractURI(string calldata _uri) external;
                            /// @dev Emitted when the contract URI is updated.
                            event ContractURIUpdated(string prevURI, string newURI);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        /**
                         *  Thirdweb's `DelayedReveal` is a contract extension for base NFT contracts. It lets you create batches of
                         *  'delayed-reveal' NFTs. You can learn more about the usage of delayed reveal NFTs here - https://blog.thirdweb.com/delayed-reveal-nfts
                         */
                        interface IDelayedReveal {
                            /// @dev Emitted when tokens are revealed.
                            event TokenURIRevealed(uint256 indexed index, string revealedURI);
                            /**
                             *  @notice Reveals a batch of delayed reveal NFTs.
                             *
                             *  @param identifier The ID for the batch of delayed-reveal NFTs to reveal.
                             *
                             *  @param key        The key with which the base URI for the relevant batch of NFTs was encrypted.
                             */
                            function reveal(uint256 identifier, bytes calldata key) external returns (string memory revealedURI);
                            /**
                             *  @notice Performs XOR encryption/decryption.
                             *
                             *  @param data The data to encrypt. In the case of delayed-reveal NFTs, this is the "revealed" state
                             *              base URI of the relevant batch of NFTs.
                             *
                             *  @param key  The key with which to encrypt data
                             */
                            function encryptDecrypt(bytes memory data, bytes calldata key) external pure returns (bytes memory result);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./IClaimConditionMultiPhase.sol";
                        /**
                         *  The interface `IDrop` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
                         *
                         *  An authorized wallet can set a series of claim conditions, ordered by their respective `startTimestamp`.
                         *  A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
                         *  or added to by the contract admin. At any moment, there is only one active claim condition.
                         */
                        interface IDrop is IClaimConditionMultiPhase {
                            /**
                             *  @param proof Proof of concerned wallet's inclusion in an allowlist.
                             *  @param quantityLimitPerWallet The total quantity of tokens the allowlisted wallet is eligible to claim over time.
                             *  @param pricePerToken The price per token the allowlisted wallet must pay to claim tokens.
                             *  @param currency The currency in which the allowlisted wallet must pay the price for claiming tokens.
                             */
                            struct AllowlistProof {
                                bytes32[] proof;
                                uint256 quantityLimitPerWallet;
                                uint256 pricePerToken;
                                address currency;
                            }
                            /// @notice Emitted when tokens are claimed via `claim`.
                            event TokensClaimed(
                                uint256 indexed claimConditionIndex,
                                address indexed claimer,
                                address indexed receiver,
                                uint256 startTokenId,
                                uint256 quantityClaimed
                            );
                            /// @notice Emitted when the contract's claim conditions are updated.
                            event ClaimConditionsUpdated(ClaimCondition[] claimConditions, bool resetEligibility);
                            /**
                             *  @notice Lets an account claim a given quantity of NFTs.
                             *
                             *  @param receiver                       The receiver of the NFTs to claim.
                             *  @param quantity                       The quantity of NFTs to claim.
                             *  @param currency                       The currency in which to pay for the claim.
                             *  @param pricePerToken                  The price per token to pay for the claim.
                             *  @param allowlistProof                 The proof of the claimer's inclusion in the merkle root allowlist
                             *                                        of the claim conditions that apply.
                             *  @param data                           Arbitrary bytes data that can be leveraged in the implementation of this interface.
                             */
                            function claim(
                                address receiver,
                                uint256 quantity,
                                address currency,
                                uint256 pricePerToken,
                                AllowlistProof calldata allowlistProof,
                                bytes memory data
                            ) external payable;
                            /**
                             *  @notice Lets a contract admin (account with `DEFAULT_ADMIN_ROLE`) set claim conditions.
                             *
                             *  @param phases                   Claim conditions in ascending order by `startTimestamp`.
                             *
                             *  @param resetClaimEligibility    Whether to honor the restrictions applied to wallets who have claimed tokens in the current conditions,
                             *                                  in the new claim conditions being set.
                             *
                             */
                            function setClaimConditions(ClaimCondition[] calldata phases, bool resetClaimEligibility) external;
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        /**
                         *  Thirdweb's `LazyMint` is a contract extension for any base NFT contract. It lets you 'lazy mint' any number of NFTs
                         *  at once. Here, 'lazy mint' means defining the metadata for particular tokenIds of your NFT contract, without actually
                         *  minting a non-zero balance of NFTs of those tokenIds.
                         */
                        interface ILazyMint {
                            /// @dev Emitted when tokens are lazy minted.
                            event TokensLazyMinted(uint256 indexed startTokenId, uint256 endTokenId, string baseURI, bytes encryptedBaseURI);
                            /**
                             *  @notice Lazy mints a given amount of NFTs.
                             *
                             *  @param amount           The number of NFTs to lazy mint.
                             *
                             *  @param baseURIForTokens The base URI for the 'n' number of NFTs being lazy minted, where the metadata for each
                             *                          of those NFTs is `${baseURIForTokens}/${tokenId}`.
                             *
                             *  @param extraData        Additional bytes data to be used at the discretion of the consumer of the contract.
                             *
                             *  @return batchId         A unique integer identifier for the batch of NFTs lazy minted together.
                             */
                            function lazyMint(
                                uint256 amount,
                                string calldata baseURIForTokens,
                                bytes calldata extraData
                            ) external returns (uint256 batchId);
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        /**
                         * @dev Provides a function to batch together multiple calls in a single external call.
                         *
                         * _Available since v4.1._
                         */
                        interface IMulticall {
                            /**
                             * @dev Receives and executes a batch of function calls on this contract.
                             */
                            function multicall(bytes[] calldata data) external returns (bytes[] memory results);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        /**
                         *  Thirdweb's `Ownable` is a contract extension to be used with any base contract. It exposes functions for setting and reading
                         *  who the 'owner' of the inheriting smart contract is, and lets the inheriting contract perform conditional logic that uses
                         *  information about who the contract's owner is.
                         */
                        interface IOwnable {
                            /// @dev Returns the owner of the contract.
                            function owner() external view returns (address);
                            /// @dev Lets a module admin set a new owner for the contract. The new owner must be a module admin.
                            function setOwner(address _newOwner) external;
                            /// @dev Emitted when a new Owner is set.
                            event OwnerUpdated(address indexed prevOwner, address indexed newOwner);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        /**
                         * @dev External interface of AccessControl declared to support ERC165 detection.
                         */
                        interface IPermissions {
                            /**
                             * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
                             *
                             * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
                             * {RoleAdminChanged} not being emitted signaling this.
                             *
                             * _Available since v3.1._
                             */
                            event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
                            /**
                             * @dev Emitted when `account` is granted `role`.
                             *
                             * `sender` is the account that originated the contract call, an admin role
                             * bearer except when using {AccessControl-_setupRole}.
                             */
                            event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
                            /**
                             * @dev Emitted when `account` is revoked `role`.
                             *
                             * `sender` is the account that originated the contract call:
                             *   - if using `revokeRole`, it is the admin role bearer
                             *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
                             */
                            event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
                            /**
                             * @dev Returns `true` if `account` has been granted `role`.
                             */
                            function hasRole(bytes32 role, address account) external view returns (bool);
                            /**
                             * @dev Returns the admin role that controls `role`. See {grantRole} and
                             * {revokeRole}.
                             *
                             * To change a role's admin, use {AccessControl-_setRoleAdmin}.
                             */
                            function getRoleAdmin(bytes32 role) external view returns (bytes32);
                            /**
                             * @dev Grants `role` to `account`.
                             *
                             * If `account` had not been already granted `role`, emits a {RoleGranted}
                             * event.
                             *
                             * Requirements:
                             *
                             * - the caller must have ``role``'s admin role.
                             */
                            function grantRole(bytes32 role, address account) external;
                            /**
                             * @dev Revokes `role` from `account`.
                             *
                             * If `account` had been granted `role`, emits a {RoleRevoked} event.
                             *
                             * Requirements:
                             *
                             * - the caller must have ``role``'s admin role.
                             */
                            function revokeRole(bytes32 role, address account) external;
                            /**
                             * @dev Revokes `role` from the calling account.
                             *
                             * Roles are often managed via {grantRole} and {revokeRole}: this function's
                             * purpose is to provide a mechanism for accounts to lose their privileges
                             * if they are compromised (such as when a trusted device is misplaced).
                             *
                             * If the calling account had been granted `role`, emits a {RoleRevoked}
                             * event.
                             *
                             * Requirements:
                             *
                             * - the caller must be `account`.
                             */
                            function renounceRole(bytes32 role, address account) external;
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./IPermissions.sol";
                        /**
                         * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
                         */
                        interface IPermissionsEnumerable is IPermissions {
                            /**
                             * @dev Returns one of the accounts that have `role`. `index` must be a
                             * value between 0 and {getRoleMemberCount}, non-inclusive.
                             *
                             * Role bearers are not sorted in any particular way, and their ordering may
                             * change at any point.
                             *
                             * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
                             * you perform all queries on the same block. See the following
                             * [forum post](https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296)
                             * for more information.
                             */
                            function getRoleMember(bytes32 role, uint256 index) external view returns (address);
                            /**
                             * @dev Returns the number of accounts that have `role`. Can be used
                             * together with {getRoleMember} to enumerate all bearers of a role.
                             */
                            function getRoleMemberCount(bytes32 role) external view returns (uint256);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        /**
                         *  Thirdweb's `PlatformFee` is a contract extension to be used with any base contract. It exposes functions for setting and reading
                         *  the recipient of platform fee and the platform fee basis points, and lets the inheriting contract perform conditional logic
                         *  that uses information about platform fees, if desired.
                         */
                        interface IPlatformFee {
                            /// @dev Fee type variants: percentage fee and flat fee
                            enum PlatformFeeType {
                                Bps,
                                Flat
                            }
                            /// @dev Returns the platform fee bps and recipient.
                            function getPlatformFeeInfo() external view returns (address, uint16);
                            /// @dev Lets a module admin update the fees on primary sales.
                            function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external;
                            /// @dev Emitted when fee on primary sales is updated.
                            event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps);
                            /// @dev Emitted when the flat platform fee is updated.
                            event FlatPlatformFeeUpdated(address platformFeeRecipient, uint256 flatFee);
                            /// @dev Emitted when the platform fee type is updated.
                            event PlatformFeeTypeUpdated(PlatformFeeType feeType);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        /**
                         *  Thirdweb's `Primary` is a contract extension to be used with any base contract. It exposes functions for setting and reading
                         *  the recipient of primary sales, and lets the inheriting contract perform conditional logic that uses information about
                         *  primary sales, if desired.
                         */
                        interface IPrimarySale {
                            /// @dev The adress that receives all primary sales value.
                            function primarySaleRecipient() external view returns (address);
                            /// @dev Lets a module admin set the default recipient of all primary sales.
                            function setPrimarySaleRecipient(address _saleRecipient) external;
                            /// @dev Emitted when a new sale recipient is set.
                            event PrimarySaleRecipientUpdated(address indexed recipient);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "../../eip/interface/IERC2981.sol";
                        /**
                         *  Thirdweb's `Royalty` is a contract extension to be used with any base contract. It exposes functions for setting and reading
                         *  the recipient of royalty fee and the royalty fee basis points, and lets the inheriting contract perform conditional logic
                         *  that uses information about royalty fees, if desired.
                         *
                         *  The `Royalty` contract is ERC2981 compliant.
                         */
                        interface IRoyalty is IERC2981 {
                            struct RoyaltyInfo {
                                address recipient;
                                uint256 bps;
                            }
                            /// @dev Returns the royalty recipient and fee bps.
                            function getDefaultRoyaltyInfo() external view returns (address, uint16);
                            /// @dev Lets a module admin update the royalty bps and recipient.
                            function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) external;
                            /// @dev Lets a module admin set the royalty recipient for a particular token Id.
                            function setRoyaltyInfoForToken(uint256 tokenId, address recipient, uint256 bps) external;
                            /// @dev Returns the royalty recipient for a particular token Id.
                            function getRoyaltyInfoForToken(uint256 tokenId) external view returns (address, uint16);
                            /// @dev Emitted when royalty info is updated.
                            event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps);
                            /// @dev Emitted when royalty recipient for tokenId is set
                            event RoyaltyForToken(uint256 indexed tokenId, address indexed royaltyRecipient, uint256 royaltyBps);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts v4.4.0 (metatx/ERC2771Context.sol)
                        pragma solidity ^0.8.11;
                        import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
                        import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
                        /**
                         * @dev Context variant with ERC2771 support.
                         */
                        abstract contract ERC2771ContextUpgradeable is Initializable, ContextUpgradeable {
                            mapping(address => bool) private _trustedForwarder;
                            function __ERC2771Context_init(address[] memory trustedForwarder) internal onlyInitializing {
                                __Context_init_unchained();
                                __ERC2771Context_init_unchained(trustedForwarder);
                            }
                            function __ERC2771Context_init_unchained(address[] memory trustedForwarder) internal onlyInitializing {
                                for (uint256 i = 0; i < trustedForwarder.length; i++) {
                                    _trustedForwarder[trustedForwarder[i]] = true;
                                }
                            }
                            function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
                                return _trustedForwarder[forwarder];
                            }
                            function _msgSender() internal view virtual override returns (address sender) {
                                if (isTrustedForwarder(msg.sender)) {
                                    // The assembly code is more direct than the Solidity version using `abi.decode`.
                                    assembly {
                                        sender := shr(96, calldataload(sub(calldatasize(), 20)))
                                    }
                                } else {
                                    return super._msgSender();
                                }
                            }
                            function _msgData() internal view virtual override returns (bytes calldata) {
                                if (isTrustedForwarder(msg.sender)) {
                                    return msg.data[:msg.data.length - 20];
                                } else {
                                    return super._msgData();
                                }
                            }
                            uint256[49] private __gap;
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)
                        pragma solidity ^0.8.0;
                        import "../../../../../eip/interface/IERC20.sol";
                        import { Address } from "../../../../../lib/Address.sol";
                        /**
                         * @title SafeERC20
                         * @dev Wrappers around ERC20 operations that throw on failure (when the token
                         * contract returns false). Tokens that return no value (and instead revert or
                         * throw on failure) are also supported, non-reverting calls are assumed to be
                         * successful.
                         * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
                         * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
                         */
                        library SafeERC20 {
                            using Address for address;
                            function safeTransfer(IERC20 token, address to, uint256 value) internal {
                                _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                            }
                            function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                                _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                            }
                            /**
                             * @dev Deprecated. This function has issues similar to the ones found in
                             * {IERC20-approve}, and its usage is discouraged.
                             *
                             * Whenever possible, use {safeIncreaseAllowance} and
                             * {safeDecreaseAllowance} instead.
                             */
                            function safeApprove(IERC20 token, address spender, uint256 value) internal {
                                // safeApprove should only be called when setting an initial allowance,
                                // or when resetting it to zero. To increase and decrease it, use
                                // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                                require(
                                    (value == 0) || (token.allowance(address(this), spender) == 0),
                                    "SafeERC20: approve from non-zero to non-zero allowance"
                                );
                                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                            }
                            function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                                uint256 newAllowance = token.allowance(address(this), spender) + value;
                                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                            }
                            function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                                unchecked {
                                    uint256 oldAllowance = token.allowance(address(this), spender);
                                    require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                                    uint256 newAllowance = oldAllowance - value;
                                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                                }
                            }
                            /**
                             * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                             * on the return value: the return value is optional (but if data is returned, it must not be false).
                             * @param token The token targeted by the call.
                             * @param data The call data (encoded using abi.encode or one of its variants).
                             */
                            function _callOptionalReturn(IERC20 token, bytes memory data) private {
                                // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                                // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                                // the target address contains contract code and also asserts for success in the low-level call.
                                bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                                if (returndata.length > 0) {
                                    // Return data is optional
                                    require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                                }
                            }
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        interface IWETH {
                            function deposit() external payable;
                            function withdraw(uint256 amount) external;
                            function transfer(address to, uint256 value) external returns (bool);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.1;
                        /// @author thirdweb, OpenZeppelin Contracts (v4.9.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
                             *
                             * Furthermore, `isContract` will also return true if the target contract within
                             * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                             * which only has an effect at the end of a transaction.
                             * ====
                             *
                             * [IMPORTANT]
                             * ====
                             * You shouldn't rely on `isContract` to protect against flash loan attacks!
                             *
                             * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                             * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                             * constructor.
                             * ====
                             */
                            function isContract(address account) internal view returns (bool) {
                                // This method relies on extcodesize/address.code.length, which returns 0
                                // for contracts in construction, since the code is only stored at the end
                                // of the constructor execution.
                                return account.code.length > 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://consensys.net/diligence/blog/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.8.0/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 functionCallWithValue(target, data, 0, "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");
                                (bool success, bytes memory returndata) = target.call{ value: value }(data);
                                return verifyCallResultFromTarget(target, 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) {
                                (bool success, bytes memory returndata) = target.staticcall(data);
                                return verifyCallResultFromTarget(target, 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) {
                                (bool success, bytes memory returndata) = target.delegatecall(data);
                                return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                            }
                            /**
                             * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                             * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                             *
                             * _Available since v4.8._
                             */
                            function verifyCallResultFromTarget(
                                address target,
                                bool success,
                                bytes memory returndata,
                                string memory errorMessage
                            ) internal view returns (bytes memory) {
                                if (success) {
                                    if (returndata.length == 0) {
                                        // only check isContract if the call was successful and the return data is empty
                                        // otherwise we already know that it was a contract
                                        require(isContract(target), "Address: call to non-contract");
                                    }
                                    return returndata;
                                } else {
                                    _revert(returndata, errorMessage);
                                }
                            }
                            /**
                             * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                             * revert reason or 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 {
                                    _revert(returndata, errorMessage);
                                }
                            }
                            function _revert(bytes memory returndata, string memory errorMessage) private pure {
                                // 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
                                    /// @solidity memory-safe-assembly
                                    assembly {
                                        let returndata_size := mload(returndata)
                                        revert(add(32, returndata), returndata_size)
                                    }
                                } else {
                                    revert(errorMessage);
                                }
                            }
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        // Helper interfaces
                        import { IWETH } from "../infra/interface/IWETH.sol";
                        import { SafeERC20, IERC20 } from "../external-deps/openzeppelin/token/ERC20/utils/SafeERC20.sol";
                        library CurrencyTransferLib {
                            using SafeERC20 for IERC20;
                            error CurrencyTransferLibMismatchedValue(uint256 expected, uint256 actual);
                            error CurrencyTransferLibFailedNativeTransfer(address recipient, uint256 value);
                            /// @dev The address interpreted as native token of the chain.
                            address public constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                            /// @dev Transfers a given amount of currency.
                            function transferCurrency(address _currency, address _from, address _to, uint256 _amount) internal {
                                if (_amount == 0) {
                                    return;
                                }
                                if (_currency == NATIVE_TOKEN) {
                                    safeTransferNativeToken(_to, _amount);
                                } else {
                                    safeTransferERC20(_currency, _from, _to, _amount);
                                }
                            }
                            /// @dev Transfers a given amount of currency. (With native token wrapping)
                            function transferCurrencyWithWrapper(
                                address _currency,
                                address _from,
                                address _to,
                                uint256 _amount,
                                address _nativeTokenWrapper
                            ) internal {
                                if (_amount == 0) {
                                    return;
                                }
                                if (_currency == NATIVE_TOKEN) {
                                    if (_from == address(this)) {
                                        // withdraw from weth then transfer withdrawn native token to recipient
                                        IWETH(_nativeTokenWrapper).withdraw(_amount);
                                        safeTransferNativeTokenWithWrapper(_to, _amount, _nativeTokenWrapper);
                                    } else if (_to == address(this)) {
                                        // store native currency in weth
                                        if (_amount != msg.value) {
                                            revert CurrencyTransferLibMismatchedValue(msg.value, _amount);
                                        }
                                        IWETH(_nativeTokenWrapper).deposit{ value: _amount }();
                                    } else {
                                        safeTransferNativeTokenWithWrapper(_to, _amount, _nativeTokenWrapper);
                                    }
                                } else {
                                    safeTransferERC20(_currency, _from, _to, _amount);
                                }
                            }
                            /// @dev Transfer `amount` of ERC20 token from `from` to `to`.
                            function safeTransferERC20(address _currency, address _from, address _to, uint256 _amount) internal {
                                if (_from == _to) {
                                    return;
                                }
                                if (_from == address(this)) {
                                    IERC20(_currency).safeTransfer(_to, _amount);
                                } else {
                                    IERC20(_currency).safeTransferFrom(_from, _to, _amount);
                                }
                            }
                            /// @dev Transfers `amount` of native token to `to`.
                            function safeTransferNativeToken(address to, uint256 value) internal {
                                // solhint-disable avoid-low-level-calls
                                // slither-disable-next-line low-level-calls
                                (bool success, ) = to.call{ value: value }("");
                                if (!success) {
                                    revert CurrencyTransferLibFailedNativeTransfer(to, value);
                                }
                            }
                            /// @dev Transfers `amount` of native token to `to`. (With native token wrapping)
                            function safeTransferNativeTokenWithWrapper(address to, uint256 value, address _nativeTokenWrapper) internal {
                                // solhint-disable avoid-low-level-calls
                                // slither-disable-next-line low-level-calls
                                (bool success, ) = to.call{ value: value }("");
                                if (!success) {
                                    IWETH(_nativeTokenWrapper).deposit{ value: value }();
                                    IERC20(_nativeTokenWrapper).safeTransfer(to, value);
                                }
                            }
                        }
                        // SPDX-License-Identifier: Apache 2.0
                        pragma solidity ^0.8.0;
                        /// @author OpenZeppelin, thirdweb
                        library MerkleProof {
                            function verify(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool, uint256) {
                                bytes32 computedHash = leaf;
                                uint256 index = 0;
                                for (uint256 i = 0; i < proof.length; i++) {
                                    index *= 2;
                                    bytes32 proofElement = proof[i];
                                    if (computedHash <= proofElement) {
                                        // Hash(current computed hash + current element of the proof)
                                        computedHash = _efficientHash(computedHash, proofElement);
                                    } else {
                                        // Hash(current element of the proof + current computed hash)
                                        computedHash = _efficientHash(proofElement, computedHash);
                                        index += 1;
                                    }
                                }
                                // Check if the computed hash (root) is equal to the provided root
                                return (computedHash == root, index);
                            }
                            /**
                             * @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
                             */
                            function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
                                /// @solidity memory-safe-assembly
                                assembly {
                                    mstore(0x00, a)
                                    mstore(0x20, b)
                                    value := keccak256(0x00, 0x40)
                                }
                            }
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        /**
                         * @dev String operations.
                         */
                        library Strings {
                            bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
                            /**
                             * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                             */
                            function toString(uint256 value) internal pure returns (string memory) {
                                // Inspired by OraclizeAPI's implementation - MIT licence
                                // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
                                if (value == 0) {
                                    return "0";
                                }
                                uint256 temp = value;
                                uint256 digits;
                                while (temp != 0) {
                                    digits++;
                                    temp /= 10;
                                }
                                bytes memory buffer = new bytes(digits);
                                while (value != 0) {
                                    digits -= 1;
                                    buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                                    value /= 10;
                                }
                                return string(buffer);
                            }
                            /**
                             * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                             */
                            function toHexString(uint256 value) internal pure returns (string memory) {
                                if (value == 0) {
                                    return "0x00";
                                }
                                uint256 temp = value;
                                uint256 length = 0;
                                while (temp != 0) {
                                    length++;
                                    temp >>= 8;
                                }
                                return toHexString(value, length);
                            }
                            /**
                             * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                             */
                            function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                                bytes memory buffer = new bytes(2 * length + 2);
                                buffer[0] = "0";
                                buffer[1] = "x";
                                for (uint256 i = 2 * length + 1; i > 1; --i) {
                                    buffer[i] = _HEX_SYMBOLS[value & 0xf];
                                    value >>= 4;
                                }
                                require(value == 0, "Strings: hex length insufficient");
                                return string(buffer);
                            }
                            /// @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.
                                }
                            }
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.11;
                        /// @author thirdweb
                        //   $$\\     $$\\       $$\\                 $$\\                         $$\\
                        //   $$ |    $$ |      \\__|                $$ |                        $$ |
                        // $$$$$$\\   $$$$$$$\\  $$\\  $$$$$$\\   $$$$$$$ |$$\\  $$\\  $$\\  $$$$$$\\  $$$$$$$\\
                        // \\_$$  _|  $$  __$$\\ $$ |$$  __$$\\ $$  __$$ |$$ | $$ | $$ |$$  __$$\\ $$  __$$\\
                        //   $$ |    $$ |  $$ |$$ |$$ |  \\__|$$ /  $$ |$$ | $$ | $$ |$$$$$$$$ |$$ |  $$ |
                        //   $$ |$$\\ $$ |  $$ |$$ |$$ |      $$ |  $$ |$$ | $$ | $$ |$$   ____|$$ |  $$ |
                        //   \\$$$$  |$$ |  $$ |$$ |$$ |      \\$$$$$$$ |\\$$$$$\\$$$$  |\\$$$$$$$\\ $$$$$$$  |
                        //    \\____/ \\__|  \\__|\\__|\\__|       \\_______| \\_____\\____/  \\_______|\\_______/
                        //  ==========  External imports    ==========
                        import "../../extension/Multicall.sol";
                        import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";
                        import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol";
                        import "../../eip/ERC721AVirtualApproveUpgradeable.sol";
                        //  ==========  Internal imports    ==========
                        import "../../external-deps/openzeppelin/metatx/ERC2771ContextUpgradeable.sol";
                        import "../../lib/CurrencyTransferLib.sol";
                        //  ==========  Features    ==========
                        import "../../extension/ContractMetadata.sol";
                        import "../../extension/PlatformFee.sol";
                        import "../../extension/Royalty.sol";
                        import "../../extension/PrimarySale.sol";
                        import "../../extension/Ownable.sol";
                        import "../../extension/DelayedReveal.sol";
                        import "../../extension/LazyMint.sol";
                        import "../../extension/PermissionsEnumerable.sol";
                        import "../../extension/Drop.sol";
                        contract DropERC721 is
                            Initializable,
                            ContractMetadata,
                            PlatformFee,
                            Royalty,
                            PrimarySale,
                            Ownable,
                            DelayedReveal,
                            LazyMint,
                            PermissionsEnumerable,
                            Drop,
                            ERC2771ContextUpgradeable,
                            Multicall,
                            ERC721AUpgradeable
                        {
                            using StringsUpgradeable for uint256;
                            /*///////////////////////////////////////////////////////////////
                                                    State variables
                            //////////////////////////////////////////////////////////////*/
                            /// @dev Only transfers to or from TRANSFER_ROLE holders are valid, when transfers are restricted.
                            bytes32 private transferRole;
                            /// @dev Only MINTER_ROLE holders can sign off on `MintRequest`s and lazy mint tokens.
                            bytes32 private minterRole;
                            /// @dev Only METADATA_ROLE holders can reveal the URI for a batch of delayed reveal NFTs, and update or freeze batch metadata.
                            bytes32 private metadataRole;
                            /// @dev Max bps in the thirdweb system.
                            uint256 private constant MAX_BPS = 10_000;
                            address public constant DEFAULT_FEE_RECIPIENT = 0x1Af20C6B23373350aD464700B5965CE4B0D2aD94;
                            uint16 private constant DEFAULT_FEE_BPS = 50;
                            /// @dev Global max total supply of NFTs.
                            uint256 public maxTotalSupply;
                            /// @dev Emitted when the global max supply of tokens is updated.
                            event MaxTotalSupplyUpdated(uint256 maxTotalSupply);
                            /*///////////////////////////////////////////////////////////////
                                            Constructor + initializer logic
                            //////////////////////////////////////////////////////////////*/
                            constructor() initializer {}
                            /// @dev Initializes the contract, like a constructor.
                            function initialize(
                                address _defaultAdmin,
                                string memory _name,
                                string memory _symbol,
                                string memory _contractURI,
                                address[] memory _trustedForwarders,
                                address _saleRecipient,
                                address _royaltyRecipient,
                                uint128 _royaltyBps,
                                uint128 _platformFeeBps,
                                address _platformFeeRecipient
                            ) external initializer {
                                bytes32 _transferRole = keccak256("TRANSFER_ROLE");
                                bytes32 _minterRole = keccak256("MINTER_ROLE");
                                bytes32 _metadataRole = keccak256("METADATA_ROLE");
                                // Initialize inherited contracts, most base-like -> most derived.
                                __ERC2771Context_init(_trustedForwarders);
                                __ERC721A_init(_name, _symbol);
                                _setupContractURI(_contractURI);
                                _setupOwner(_defaultAdmin);
                                _setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin);
                                _setupRole(_minterRole, _defaultAdmin);
                                _setupRole(_transferRole, _defaultAdmin);
                                _setupRole(_transferRole, address(0));
                                _setupRole(_metadataRole, _defaultAdmin);
                                _setRoleAdmin(_metadataRole, _metadataRole);
                                _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps);
                                _setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps);
                                _setupPrimarySaleRecipient(_saleRecipient);
                                transferRole = _transferRole;
                                minterRole = _minterRole;
                                metadataRole = _metadataRole;
                            }
                            /*///////////////////////////////////////////////////////////////
                                                ERC 165 / 721 / 2981 logic
                            //////////////////////////////////////////////////////////////*/
                            /// @dev Returns the URI for a given tokenId.
                            function tokenURI(uint256 _tokenId) public view override returns (string memory) {
                                (uint256 batchId, ) = _getBatchId(_tokenId);
                                string memory batchUri = _getBaseURI(_tokenId);
                                if (isEncryptedBatch(batchId)) {
                                    return string(abi.encodePacked(batchUri, "0"));
                                } else {
                                    return string(abi.encodePacked(batchUri, _tokenId.toString()));
                                }
                            }
                            /// @dev See ERC 165
                            function supportsInterface(
                                bytes4 interfaceId
                            ) public view virtual override(ERC721AUpgradeable, IERC165) returns (bool) {
                                return super.supportsInterface(interfaceId) || type(IERC2981Upgradeable).interfaceId == interfaceId;
                            }
                            /*///////////////////////////////////////////////////////////////
                                                Contract identifiers
                            //////////////////////////////////////////////////////////////*/
                            function contractType() external pure returns (bytes32) {
                                return bytes32("DropERC721");
                            }
                            function contractVersion() external pure returns (uint8) {
                                return uint8(4);
                            }
                            /*///////////////////////////////////////////////////////////////
                                            Lazy minting + delayed-reveal logic
                            //////////////////////////////////////////////////////////////*/
                            /**
                             *  @dev Lets an account with `MINTER_ROLE` lazy mint 'n' NFTs.
                             *       The URIs for each token is the provided `_baseURIForTokens` + `{tokenId}`.
                             */
                            function lazyMint(
                                uint256 _amount,
                                string calldata _baseURIForTokens,
                                bytes calldata _data
                            ) public override returns (uint256 batchId) {
                                if (_data.length > 0) {
                                    (bytes memory encryptedURI, bytes32 provenanceHash) = abi.decode(_data, (bytes, bytes32));
                                    if (encryptedURI.length != 0 && provenanceHash != "") {
                                        _setEncryptedData(nextTokenIdToLazyMint + _amount, _data);
                                    }
                                }
                                return super.lazyMint(_amount, _baseURIForTokens, _data);
                            }
                            /// @dev Lets an account with `METADATA_ROLE` reveal the URI for a batch of 'delayed-reveal' NFTs.
                            /// @param _index the ID of a token with the desired batch.
                            /// @param _key the key to decrypt the batch's URI.
                            function reveal(
                                uint256 _index,
                                bytes calldata _key
                            ) external onlyRole(metadataRole) returns (string memory revealedURI) {
                                uint256 batchId = getBatchIdAtIndex(_index);
                                revealedURI = getRevealURI(batchId, _key);
                                _setEncryptedData(batchId, "");
                                _setBaseURI(batchId, revealedURI);
                                emit TokenURIRevealed(_index, revealedURI);
                            }
                            /**
                             * @notice Updates the base URI for a batch of tokens. Can only be called if the batch has been revealed/is not encrypted.
                             *
                             * @param _index Index of the desired batch in batchIds array
                             * @param _uri   the new base URI for the batch.
                             */
                            function updateBatchBaseURI(uint256 _index, string calldata _uri) external onlyRole(metadataRole) {
                                require(!isEncryptedBatch(getBatchIdAtIndex(_index)), "Encrypted batch");
                                uint256 batchId = getBatchIdAtIndex(_index);
                                _setBaseURI(batchId, _uri);
                            }
                            /**
                             * @notice Freezes the base URI for a batch of tokens.
                             *
                             * @param _index Index of the desired batch in batchIds array.
                             */
                            function freezeBatchBaseURI(uint256 _index) external onlyRole(metadataRole) {
                                require(!isEncryptedBatch(getBatchIdAtIndex(_index)), "Encrypted batch");
                                uint256 batchId = getBatchIdAtIndex(_index);
                                _freezeBaseURI(batchId);
                            }
                            /*///////////////////////////////////////////////////////////////
                                                Setter functions
                            //////////////////////////////////////////////////////////////*/
                            /// @dev Lets a contract admin set the global maximum supply for collection's NFTs.
                            function setMaxTotalSupply(uint256 _maxTotalSupply) external onlyRole(DEFAULT_ADMIN_ROLE) {
                                maxTotalSupply = _maxTotalSupply;
                                emit MaxTotalSupplyUpdated(_maxTotalSupply);
                            }
                            /*///////////////////////////////////////////////////////////////
                                                Internal functions
                            //////////////////////////////////////////////////////////////*/
                            /// @dev Runs before every `claim` function call.
                            function _beforeClaim(
                                address,
                                uint256 _quantity,
                                address,
                                uint256,
                                AllowlistProof calldata,
                                bytes memory
                            ) internal view override {
                                require(_currentIndex + _quantity <= nextTokenIdToLazyMint, "!Tokens");
                                require(maxTotalSupply == 0 || _currentIndex + _quantity <= maxTotalSupply, "!Supply");
                            }
                            /// @dev Collects and distributes the primary sale value of NFTs being claimed.
                            function _collectPriceOnClaim(
                                address _primarySaleRecipient,
                                uint256 _quantityToClaim,
                                address _currency,
                                uint256 _pricePerToken
                            ) internal override {
                                if (_pricePerToken == 0) {
                                    require(msg.value == 0, "!V");
                                    return;
                                }
                                (address platformFeeRecipient, uint16 platformFeeBps) = getPlatformFeeInfo();
                                address saleRecipient = _primarySaleRecipient == address(0) ? primarySaleRecipient() : _primarySaleRecipient;
                                uint256 totalPrice = _quantityToClaim * _pricePerToken;
                                uint256 platformFeesTw = (totalPrice * DEFAULT_FEE_BPS) / MAX_BPS;
                                uint256 platformFees = (totalPrice * platformFeeBps) / MAX_BPS;
                                bool validMsgValue;
                                if (_currency == CurrencyTransferLib.NATIVE_TOKEN) {
                                    validMsgValue = msg.value == totalPrice;
                                } else {
                                    validMsgValue = msg.value == 0;
                                }
                                require(validMsgValue, "!V");
                                CurrencyTransferLib.transferCurrency(_currency, _msgSender(), DEFAULT_FEE_RECIPIENT, platformFeesTw);
                                CurrencyTransferLib.transferCurrency(_currency, _msgSender(), platformFeeRecipient, platformFees);
                                CurrencyTransferLib.transferCurrency(
                                    _currency,
                                    _msgSender(),
                                    saleRecipient,
                                    totalPrice - platformFees - platformFeesTw
                                );
                            }
                            /// @dev Transfers the NFTs being claimed.
                            function _transferTokensOnClaim(
                                address _to,
                                uint256 _quantityBeingClaimed
                            ) internal override returns (uint256 startTokenId) {
                                startTokenId = _currentIndex;
                                _safeMint(_to, _quantityBeingClaimed);
                            }
                            /// @dev Checks whether platform fee info can be set in the given execution context.
                            function _canSetPlatformFeeInfo() internal view override returns (bool) {
                                return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                            }
                            /// @dev Checks whether primary sale recipient can be set in the given execution context.
                            function _canSetPrimarySaleRecipient() internal view override returns (bool) {
                                return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                            }
                            /// @dev Checks whether owner can be set in the given execution context.
                            function _canSetOwner() internal view override returns (bool) {
                                return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                            }
                            /// @dev Checks whether royalty info can be set in the given execution context.
                            function _canSetRoyaltyInfo() internal view override returns (bool) {
                                return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                            }
                            /// @dev Checks whether contract metadata can be set in the given execution context.
                            function _canSetContractURI() internal view override returns (bool) {
                                return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                            }
                            /// @dev Checks whether platform fee info can be set in the given execution context.
                            function _canSetClaimConditions() internal view override returns (bool) {
                                return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                            }
                            /// @dev Returns whether lazy minting can be done in the given execution context.
                            function _canLazyMint() internal view virtual override returns (bool) {
                                return hasRole(minterRole, _msgSender());
                            }
                            /*///////////////////////////////////////////////////////////////
                                                Miscellaneous
                            //////////////////////////////////////////////////////////////*/
                            /**
                             * Returns the total amount of tokens minted in the contract.
                             */
                            function totalMinted() external view returns (uint256) {
                                return _totalMinted();
                            }
                            /// @dev The tokenId of the next NFT that will be minted / lazy minted.
                            function nextTokenIdToMint() external view returns (uint256) {
                                return nextTokenIdToLazyMint;
                            }
                            /// @dev The next token ID of the NFT that can be claimed.
                            function nextTokenIdToClaim() external view returns (uint256) {
                                return _currentIndex;
                            }
                            /// @dev Burns `tokenId`. See {ERC721-_burn}.
                            function burn(uint256 tokenId) external virtual {
                                // note: ERC721AUpgradeable's `_burn(uint256,bool)` internally checks for token approvals.
                                _burn(tokenId, true);
                            }
                            /// @dev See {ERC721-_beforeTokenTransfer}.
                            function _beforeTokenTransfers(
                                address from,
                                address to,
                                uint256 startTokenId,
                                uint256 quantity
                            ) internal virtual override {
                                super._beforeTokenTransfers(from, to, startTokenId, quantity);
                                // if transfer is restricted on the contract, we still want to allow burning and minting
                                if (!hasRole(transferRole, address(0)) && from != address(0) && to != address(0)) {
                                    if (!hasRole(transferRole, from) && !hasRole(transferRole, to)) {
                                        revert("!Transfer-Role");
                                    }
                                }
                            }
                            function _dropMsgSender() internal view virtual override returns (address) {
                                return _msgSender();
                            }
                            function _msgSender()
                                internal
                                view
                                virtual
                                override(ContextUpgradeable, ERC2771ContextUpgradeable, Multicall)
                                returns (address sender)
                            {
                                return ERC2771ContextUpgradeable._msgSender();
                            }
                            function _msgData()
                                internal
                                view
                                virtual
                                override(ContextUpgradeable, ERC2771ContextUpgradeable)
                                returns (bytes calldata)
                            {
                                return ERC2771ContextUpgradeable._msgData();
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        // ERC721A Contracts v3.3.0
                        // Creator: Chiru Labs
                        pragma solidity ^0.8.4;
                        import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
                        import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721MetadataUpgradeable.sol";
                        /**
                         * @dev Interface of an ERC721A compliant contract.
                         */
                        interface IERC721AUpgradeable is IERC721Upgradeable, IERC721MetadataUpgradeable {
                            /**
                             * The caller must own the token or be an approved operator.
                             */
                            error ApprovalCallerNotOwnerNorApproved();
                            /**
                             * The token does not exist.
                             */
                            error ApprovalQueryForNonexistentToken();
                            /**
                             * The caller cannot approve to their own address.
                             */
                            error ApproveToCaller();
                            /**
                             * The caller cannot approve to the current owner.
                             */
                            error ApprovalToCurrentOwner();
                            /**
                             * Cannot query the balance for the zero address.
                             */
                            error BalanceQueryForZeroAddress();
                            /**
                             * Cannot mint to the zero address.
                             */
                            error MintToZeroAddress();
                            /**
                             * The quantity of tokens minted must be more than zero.
                             */
                            error MintZeroQuantity();
                            /**
                             * The token does not exist.
                             */
                            error OwnerQueryForNonexistentToken();
                            /**
                             * The caller must own the token or be an approved operator.
                             */
                            error TransferCallerNotOwnerNorApproved();
                            /**
                             * The token must be owned by `from`.
                             */
                            error TransferFromIncorrectOwner();
                            /**
                             * Cannot safely transfer to a contract that does not implement the ERC721Receiver interface.
                             */
                            error TransferToNonERC721ReceiverImplementer();
                            /**
                             * Cannot transfer to the zero address.
                             */
                            error TransferToZeroAddress();
                            /**
                             * The token does not exist.
                             */
                            error URIQueryForNonexistentToken();
                            // Compiler will pack this into a single 256bit word.
                            struct TokenOwnership {
                                // The address of the owner.
                                address addr;
                                // Keeps track of the start time of ownership with minimal overhead for tokenomics.
                                uint64 startTimestamp;
                                // Whether the token has been burned.
                                bool burned;
                            }
                            // Compiler will pack this into a single 256bit word.
                            struct AddressData {
                                // Realistically, 2**64-1 is more than enough.
                                uint64 balance;
                                // Keeps track of mint count with minimal overhead for tokenomics.
                                uint64 numberMinted;
                                // Keeps track of burn count with minimal overhead for tokenomics.
                                uint64 numberBurned;
                                // For miscellaneous variable(s) pertaining to the address
                                // (e.g. number of whitelist mint slots used).
                                // If there are multiple variables, please pack them into a uint64.
                                uint64 aux;
                            }
                            /**
                             * @dev Returns the total amount of tokens stored by the contract.
                             * 
                             * Burned tokens are calculated here, use `_totalMinted()` if you want to count just minted tokens.
                             */
                            function totalSupply() external view returns (uint256);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC2981.sol)
                        pragma solidity ^0.8.0;
                        import "../utils/introspection/IERC165Upgradeable.sol";
                        /**
                         * @dev Interface for the NFT Royalty Standard.
                         *
                         * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
                         * support for royalty payments across all NFT marketplaces and ecosystem participants.
                         *
                         * _Available since v4.5._
                         */
                        interface IERC2981Upgradeable is IERC165Upgradeable {
                            /**
                             * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
                             * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
                             */
                            function royaltyInfo(
                                uint256 tokenId,
                                uint256 salePrice
                            ) external view returns (address receiver, uint256 royaltyAmount);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
                        pragma solidity ^0.8.2;
                        import "../../utils/AddressUpgradeable.sol";
                        /**
                         * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
                         * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
                         * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
                         * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
                         *
                         * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
                         * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
                         * case an upgrade adds a module that needs to be initialized.
                         *
                         * For example:
                         *
                         * [.hljs-theme-light.nopadding]
                         * ```solidity
                         * contract MyToken is ERC20Upgradeable {
                         *     function initialize() initializer public {
                         *         __ERC20_init("MyToken", "MTK");
                         *     }
                         * }
                         *
                         * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
                         *     function initializeV2() reinitializer(2) public {
                         *         __ERC20Permit_init("MyToken");
                         *     }
                         * }
                         * ```
                         *
                         * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
                         * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
                         *
                         * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
                         * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
                         *
                         * [CAUTION]
                         * ====
                         * Avoid leaving a contract uninitialized.
                         *
                         * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
                         * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
                         * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
                         *
                         * [.hljs-theme-light.nopadding]
                         * ```
                         * /// @custom:oz-upgrades-unsafe-allow constructor
                         * constructor() {
                         *     _disableInitializers();
                         * }
                         * ```
                         * ====
                         */
                        abstract contract Initializable {
                            /**
                             * @dev Indicates that the contract has been initialized.
                             * @custom:oz-retyped-from bool
                             */
                            uint8 private _initialized;
                            /**
                             * @dev Indicates that the contract is in the process of being initialized.
                             */
                            bool private _initializing;
                            /**
                             * @dev Triggered when the contract has been initialized or reinitialized.
                             */
                            event Initialized(uint8 version);
                            /**
                             * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
                             * `onlyInitializing` functions can be used to initialize parent contracts.
                             *
                             * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
                             * constructor.
                             *
                             * Emits an {Initialized} event.
                             */
                            modifier initializer() {
                                bool isTopLevelCall = !_initializing;
                                require(
                                    (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                                    "Initializable: contract is already initialized"
                                );
                                _initialized = 1;
                                if (isTopLevelCall) {
                                    _initializing = true;
                                }
                                _;
                                if (isTopLevelCall) {
                                    _initializing = false;
                                    emit Initialized(1);
                                }
                            }
                            /**
                             * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
                             * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
                             * used to initialize parent contracts.
                             *
                             * A reinitializer may be used after the original initialization step. This is essential to configure modules that
                             * are added through upgrades and that require initialization.
                             *
                             * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
                             * cannot be nested. If one is invoked in the context of another, execution will revert.
                             *
                             * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
                             * a contract, executing them in the right order is up to the developer or operator.
                             *
                             * WARNING: setting the version to 255 will prevent any future reinitialization.
                             *
                             * Emits an {Initialized} event.
                             */
                            modifier reinitializer(uint8 version) {
                                require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
                                _initialized = version;
                                _initializing = true;
                                _;
                                _initializing = false;
                                emit Initialized(version);
                            }
                            /**
                             * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
                             * {initializer} and {reinitializer} modifiers, directly or indirectly.
                             */
                            modifier onlyInitializing() {
                                require(_initializing, "Initializable: contract is not initializing");
                                _;
                            }
                            /**
                             * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
                             * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
                             * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
                             * through proxies.
                             *
                             * Emits an {Initialized} event the first time it is successfully executed.
                             */
                            function _disableInitializers() internal virtual {
                                require(!_initializing, "Initializable: contract is initializing");
                                if (_initialized != type(uint8).max) {
                                    _initialized = type(uint8).max;
                                    emit Initialized(type(uint8).max);
                                }
                            }
                            /**
                             * @dev Returns the highest version that has been initialized. See {reinitializer}.
                             */
                            function _getInitializedVersion() internal view returns (uint8) {
                                return _initialized;
                            }
                            /**
                             * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
                             */
                            function _isInitializing() internal view returns (bool) {
                                return _initializing;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v4.6.0) (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 IERC721ReceiverUpgradeable {
                            /**
                             * @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 `IERC721Receiver.onERC721Received.selector`.
                             */
                            function onERC721Received(
                                address operator,
                                address from,
                                uint256 tokenId,
                                bytes calldata data
                            ) external returns (bytes4);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)
                        pragma solidity ^0.8.0;
                        import "../../utils/introspection/IERC165Upgradeable.sol";
                        /**
                         * @dev Required interface of an ERC721 compliant contract.
                         */
                        interface IERC721Upgradeable is IERC165Upgradeable {
                            /**
                             * @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`.
                             *
                             * 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;
                            /**
                             * @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 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: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
                             * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
                             * understand this adds an external call which potentially creates a reentrancy vulnerability.
                             *
                             * 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 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 the account approved for `tokenId` token.
                             *
                             * Requirements:
                             *
                             * - `tokenId` must exist.
                             */
                            function getApproved(uint256 tokenId) external view returns (address operator);
                            /**
                             * @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);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
                        pragma solidity ^0.8.0;
                        import "../IERC721Upgradeable.sol";
                        /**
                         * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
                         * @dev See https://eips.ethereum.org/EIPS/eip-721
                         */
                        interface IERC721MetadataUpgradeable is IERC721Upgradeable {
                            /**
                             * @dev Returns the token collection name.
                             */
                            function name() external view returns (string memory);
                            /**
                             * @dev Returns the token collection symbol.
                             */
                            function symbol() external view returns (string memory);
                            /**
                             * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
                             */
                            function tokenURI(uint256 tokenId) external view returns (string memory);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
                        pragma solidity ^0.8.1;
                        /**
                         * @dev Collection of functions related to the address type
                         */
                        library AddressUpgradeable {
                            /**
                             * @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
                             *
                             * Furthermore, `isContract` will also return true if the target contract within
                             * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                             * which only has an effect at the end of a transaction.
                             * ====
                             *
                             * [IMPORTANT]
                             * ====
                             * You shouldn't rely on `isContract` to protect against flash loan attacks!
                             *
                             * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                             * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                             * constructor.
                             * ====
                             */
                            function isContract(address account) internal view returns (bool) {
                                // This method relies on extcodesize/address.code.length, which returns 0
                                // for contracts in construction, since the code is only stored at the end
                                // of the constructor execution.
                                return account.code.length > 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://consensys.net/diligence/blog/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.8.0/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 functionCallWithValue(target, data, 0, "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");
                                (bool success, bytes memory returndata) = target.call{value: value}(data);
                                return verifyCallResultFromTarget(target, 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) {
                                (bool success, bytes memory returndata) = target.staticcall(data);
                                return verifyCallResultFromTarget(target, 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) {
                                (bool success, bytes memory returndata) = target.delegatecall(data);
                                return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                            }
                            /**
                             * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                             * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                             *
                             * _Available since v4.8._
                             */
                            function verifyCallResultFromTarget(
                                address target,
                                bool success,
                                bytes memory returndata,
                                string memory errorMessage
                            ) internal view returns (bytes memory) {
                                if (success) {
                                    if (returndata.length == 0) {
                                        // only check isContract if the call was successful and the return data is empty
                                        // otherwise we already know that it was a contract
                                        require(isContract(target), "Address: call to non-contract");
                                    }
                                    return returndata;
                                } else {
                                    _revert(returndata, errorMessage);
                                }
                            }
                            /**
                             * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                             * revert reason or 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 {
                                    _revert(returndata, errorMessage);
                                }
                            }
                            function _revert(bytes memory returndata, string memory errorMessage) private pure {
                                // 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
                                    /// @solidity memory-safe-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/Context.sol)
                        pragma solidity ^0.8.0;
                        import "../proxy/utils/Initializable.sol";
                        /**
                         * @dev Provides information about the current execution context, including the
                         * sender of the transaction and its data. While these are generally available
                         * via msg.sender and msg.data, they should not be accessed in such a direct
                         * manner, since when dealing with meta-transactions the account sending and
                         * paying for execution may not be the actual sender (as far as an application
                         * is concerned).
                         *
                         * This contract is only required for intermediate, library-like contracts.
                         */
                        abstract contract ContextUpgradeable is Initializable {
                            function __Context_init() internal onlyInitializing {
                            }
                            function __Context_init_unchained() internal onlyInitializing {
                            }
                            function _msgSender() internal view virtual returns (address) {
                                return msg.sender;
                            }
                            function _msgData() internal view virtual returns (bytes calldata) {
                                return msg.data;
                            }
                            /**
                             * @dev This empty reserved space is put in place to allow future versions to add new
                             * variables without shifting down storage in the inheritance chain.
                             * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                             */
                            uint256[50] private __gap;
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
                        pragma solidity ^0.8.0;
                        import "./math/MathUpgradeable.sol";
                        import "./math/SignedMathUpgradeable.sol";
                        /**
                         * @dev String operations.
                         */
                        library StringsUpgradeable {
                            bytes16 private constant _SYMBOLS = "0123456789abcdef";
                            uint8 private constant _ADDRESS_LENGTH = 20;
                            /**
                             * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                             */
                            function toString(uint256 value) internal pure returns (string memory) {
                                unchecked {
                                    uint256 length = MathUpgradeable.log10(value) + 1;
                                    string memory buffer = new string(length);
                                    uint256 ptr;
                                    /// @solidity memory-safe-assembly
                                    assembly {
                                        ptr := add(buffer, add(32, length))
                                    }
                                    while (true) {
                                        ptr--;
                                        /// @solidity memory-safe-assembly
                                        assembly {
                                            mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                                        }
                                        value /= 10;
                                        if (value == 0) break;
                                    }
                                    return buffer;
                                }
                            }
                            /**
                             * @dev Converts a `int256` to its ASCII `string` decimal representation.
                             */
                            function toString(int256 value) internal pure returns (string memory) {
                                return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMathUpgradeable.abs(value))));
                            }
                            /**
                             * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                             */
                            function toHexString(uint256 value) internal pure returns (string memory) {
                                unchecked {
                                    return toHexString(value, MathUpgradeable.log256(value) + 1);
                                }
                            }
                            /**
                             * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                             */
                            function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                                bytes memory buffer = new bytes(2 * length + 2);
                                buffer[0] = "0";
                                buffer[1] = "x";
                                for (uint256 i = 2 * length + 1; i > 1; --i) {
                                    buffer[i] = _SYMBOLS[value & 0xf];
                                    value >>= 4;
                                }
                                require(value == 0, "Strings: hex length insufficient");
                                return string(buffer);
                            }
                            /**
                             * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
                             */
                            function toHexString(address addr) internal pure returns (string memory) {
                                return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
                            }
                            /**
                             * @dev Returns true if the two strings are equal.
                             */
                            function equal(string memory a, string memory b) internal pure returns (bool) {
                                return keccak256(bytes(a)) == keccak256(bytes(b));
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
                        pragma solidity ^0.8.0;
                        import "./IERC165Upgradeable.sol";
                        import "../../proxy/utils/Initializable.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 ERC165Upgradeable is Initializable, IERC165Upgradeable {
                            function __ERC165_init() internal onlyInitializing {
                            }
                            function __ERC165_init_unchained() internal onlyInitializing {
                            }
                            /**
                             * @dev See {IERC165-supportsInterface}.
                             */
                            function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                                return interfaceId == type(IERC165Upgradeable).interfaceId;
                            }
                            /**
                             * @dev This empty reserved space is put in place to allow future versions to add new
                             * variables without shifting down storage in the inheritance chain.
                             * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                             */
                            uint256[50] private __gap;
                        }
                        // 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 IERC165Upgradeable {
                            /**
                             * @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 (last updated v4.9.0) (utils/math/Math.sol)
                        pragma solidity ^0.8.0;
                        /**
                         * @dev Standard math utilities missing in the Solidity language.
                         */
                        library MathUpgradeable {
                            enum Rounding {
                                Down, // Toward negative infinity
                                Up, // Toward infinity
                                Zero // Toward zero
                            }
                            /**
                             * @dev Returns the largest of two numbers.
                             */
                            function max(uint256 a, uint256 b) internal pure returns (uint256) {
                                return a > b ? a : b;
                            }
                            /**
                             * @dev Returns the smallest of two numbers.
                             */
                            function min(uint256 a, uint256 b) internal pure returns (uint256) {
                                return a < b ? a : b;
                            }
                            /**
                             * @dev Returns the average of two numbers. The result is rounded towards
                             * zero.
                             */
                            function average(uint256 a, uint256 b) internal pure returns (uint256) {
                                // (a + b) / 2 can overflow.
                                return (a & b) + (a ^ b) / 2;
                            }
                            /**
                             * @dev Returns the ceiling of the division of two numbers.
                             *
                             * This differs from standard division with `/` in that it rounds up instead
                             * of rounding down.
                             */
                            function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                                // (a + b - 1) / b can overflow on addition, so we distribute.
                                return a == 0 ? 0 : (a - 1) / b + 1;
                            }
                            /**
                             * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
                             * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
                             * with further edits by Uniswap Labs also under MIT license.
                             */
                            function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
                                unchecked {
                                    // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                                    // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                                    // variables such that product = prod1 * 2^256 + prod0.
                                    uint256 prod0; // Least significant 256 bits of the product
                                    uint256 prod1; // Most significant 256 bits of the product
                                    assembly {
                                        let mm := mulmod(x, y, not(0))
                                        prod0 := mul(x, y)
                                        prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                                    }
                                    // Handle non-overflow cases, 256 by 256 division.
                                    if (prod1 == 0) {
                                        // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                                        // The surrounding unchecked block does not change this fact.
                                        // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                                        return prod0 / denominator;
                                    }
                                    // Make sure the result is less than 2^256. Also prevents denominator == 0.
                                    require(denominator > prod1, "Math: mulDiv overflow");
                                    ///////////////////////////////////////////////
                                    // 512 by 256 division.
                                    ///////////////////////////////////////////////
                                    // Make division exact by subtracting the remainder from [prod1 prod0].
                                    uint256 remainder;
                                    assembly {
                                        // Compute remainder using mulmod.
                                        remainder := mulmod(x, y, denominator)
                                        // Subtract 256 bit number from 512 bit number.
                                        prod1 := sub(prod1, gt(remainder, prod0))
                                        prod0 := sub(prod0, remainder)
                                    }
                                    // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                                    // See https://cs.stackexchange.com/q/138556/92363.
                                    // Does not overflow because the denominator cannot be zero at this stage in the function.
                                    uint256 twos = denominator & (~denominator + 1);
                                    assembly {
                                        // Divide denominator by twos.
                                        denominator := div(denominator, twos)
                                        // Divide [prod1 prod0] by twos.
                                        prod0 := div(prod0, twos)
                                        // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                                        twos := add(div(sub(0, twos), twos), 1)
                                    }
                                    // Shift in bits from prod1 into prod0.
                                    prod0 |= prod1 * twos;
                                    // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                                    // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                                    // four bits. That is, denominator * inv = 1 mod 2^4.
                                    uint256 inverse = (3 * denominator) ^ 2;
                                    // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                                    // in modular arithmetic, doubling the correct bits in each step.
                                    inverse *= 2 - denominator * inverse; // inverse mod 2^8
                                    inverse *= 2 - denominator * inverse; // inverse mod 2^16
                                    inverse *= 2 - denominator * inverse; // inverse mod 2^32
                                    inverse *= 2 - denominator * inverse; // inverse mod 2^64
                                    inverse *= 2 - denominator * inverse; // inverse mod 2^128
                                    inverse *= 2 - denominator * inverse; // inverse mod 2^256
                                    // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                                    // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                                    // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                                    // is no longer required.
                                    result = prod0 * inverse;
                                    return result;
                                }
                            }
                            /**
                             * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
                             */
                            function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
                                uint256 result = mulDiv(x, y, denominator);
                                if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                                    result += 1;
                                }
                                return result;
                            }
                            /**
                             * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
                             *
                             * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
                             */
                            function sqrt(uint256 a) internal pure returns (uint256) {
                                if (a == 0) {
                                    return 0;
                                }
                                // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                                //
                                // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                                // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                                //
                                // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                                // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                                // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                                //
                                // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                                uint256 result = 1 << (log2(a) >> 1);
                                // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                                // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                                // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                                // into the expected uint128 result.
                                unchecked {
                                    result = (result + a / result) >> 1;
                                    result = (result + a / result) >> 1;
                                    result = (result + a / result) >> 1;
                                    result = (result + a / result) >> 1;
                                    result = (result + a / result) >> 1;
                                    result = (result + a / result) >> 1;
                                    result = (result + a / result) >> 1;
                                    return min(result, a / result);
                                }
                            }
                            /**
                             * @notice Calculates sqrt(a), following the selected rounding direction.
                             */
                            function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                                unchecked {
                                    uint256 result = sqrt(a);
                                    return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                                }
                            }
                            /**
                             * @dev Return the log in base 2, rounded down, of a positive value.
                             * Returns 0 if given 0.
                             */
                            function log2(uint256 value) internal pure returns (uint256) {
                                uint256 result = 0;
                                unchecked {
                                    if (value >> 128 > 0) {
                                        value >>= 128;
                                        result += 128;
                                    }
                                    if (value >> 64 > 0) {
                                        value >>= 64;
                                        result += 64;
                                    }
                                    if (value >> 32 > 0) {
                                        value >>= 32;
                                        result += 32;
                                    }
                                    if (value >> 16 > 0) {
                                        value >>= 16;
                                        result += 16;
                                    }
                                    if (value >> 8 > 0) {
                                        value >>= 8;
                                        result += 8;
                                    }
                                    if (value >> 4 > 0) {
                                        value >>= 4;
                                        result += 4;
                                    }
                                    if (value >> 2 > 0) {
                                        value >>= 2;
                                        result += 2;
                                    }
                                    if (value >> 1 > 0) {
                                        result += 1;
                                    }
                                }
                                return result;
                            }
                            /**
                             * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
                             * Returns 0 if given 0.
                             */
                            function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                                unchecked {
                                    uint256 result = log2(value);
                                    return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                                }
                            }
                            /**
                             * @dev Return the log in base 10, rounded down, of a positive value.
                             * Returns 0 if given 0.
                             */
                            function log10(uint256 value) internal pure returns (uint256) {
                                uint256 result = 0;
                                unchecked {
                                    if (value >= 10 ** 64) {
                                        value /= 10 ** 64;
                                        result += 64;
                                    }
                                    if (value >= 10 ** 32) {
                                        value /= 10 ** 32;
                                        result += 32;
                                    }
                                    if (value >= 10 ** 16) {
                                        value /= 10 ** 16;
                                        result += 16;
                                    }
                                    if (value >= 10 ** 8) {
                                        value /= 10 ** 8;
                                        result += 8;
                                    }
                                    if (value >= 10 ** 4) {
                                        value /= 10 ** 4;
                                        result += 4;
                                    }
                                    if (value >= 10 ** 2) {
                                        value /= 10 ** 2;
                                        result += 2;
                                    }
                                    if (value >= 10 ** 1) {
                                        result += 1;
                                    }
                                }
                                return result;
                            }
                            /**
                             * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                             * Returns 0 if given 0.
                             */
                            function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                                unchecked {
                                    uint256 result = log10(value);
                                    return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
                                }
                            }
                            /**
                             * @dev Return the log in base 256, rounded down, of a positive value.
                             * Returns 0 if given 0.
                             *
                             * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
                             */
                            function log256(uint256 value) internal pure returns (uint256) {
                                uint256 result = 0;
                                unchecked {
                                    if (value >> 128 > 0) {
                                        value >>= 128;
                                        result += 16;
                                    }
                                    if (value >> 64 > 0) {
                                        value >>= 64;
                                        result += 8;
                                    }
                                    if (value >> 32 > 0) {
                                        value >>= 32;
                                        result += 4;
                                    }
                                    if (value >> 16 > 0) {
                                        value >>= 16;
                                        result += 2;
                                    }
                                    if (value >> 8 > 0) {
                                        result += 1;
                                    }
                                }
                                return result;
                            }
                            /**
                             * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
                             * Returns 0 if given 0.
                             */
                            function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                                unchecked {
                                    uint256 result = log256(value);
                                    return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
                                }
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
                        pragma solidity ^0.8.0;
                        /**
                         * @dev Standard signed math utilities missing in the Solidity language.
                         */
                        library SignedMathUpgradeable {
                            /**
                             * @dev Returns the largest of two signed numbers.
                             */
                            function max(int256 a, int256 b) internal pure returns (int256) {
                                return a > b ? a : b;
                            }
                            /**
                             * @dev Returns the smallest of two signed numbers.
                             */
                            function min(int256 a, int256 b) internal pure returns (int256) {
                                return a < b ? a : b;
                            }
                            /**
                             * @dev Returns the average of two signed numbers without overflow.
                             * The result is rounded towards zero.
                             */
                            function average(int256 a, int256 b) internal pure returns (int256) {
                                // Formula from the book "Hacker's Delight"
                                int256 x = (a & b) + ((a ^ b) >> 1);
                                return x + (int256(uint256(x) >> 255) & (a ^ b));
                            }
                            /**
                             * @dev Returns the absolute unsigned value of a signed value.
                             */
                            function abs(int256 n) internal pure returns (uint256) {
                                unchecked {
                                    // must be unchecked in order to support `n = type(int256).min`
                                    return uint256(n >= 0 ? n : -n);
                                }
                            }
                        }
                        

                        File 6 of 11: ExchangeV2
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.0 <0.8.0;
                        import "../utils/ContextUpgradeable.sol";
                        import "../proxy/Initializable.sol";
                        /**
                         * @dev Contract module which provides a basic access control mechanism, where
                         * there is an account (an owner) that can be granted exclusive access to
                         * specific functions.
                         *
                         * By default, the owner account will be the one that deploys the contract. This
                         * can later be changed with {transferOwnership}.
                         *
                         * This module is used through inheritance. It will make available the modifier
                         * `onlyOwner`, which can be applied to your functions to restrict their use to
                         * the owner.
                         */
                        abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
                            address private _owner;
                            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                            /**
                             * @dev Initializes the contract setting the deployer as the initial owner.
                             */
                            function __Ownable_init() internal initializer {
                                __Context_init_unchained();
                                __Ownable_init_unchained();
                            }
                            function __Ownable_init_unchained() internal initializer {
                                address msgSender = _msgSender();
                                _owner = msgSender;
                                emit OwnershipTransferred(address(0), msgSender);
                            }
                            /**
                             * @dev Returns the address of the current owner.
                             */
                            function owner() public view virtual returns (address) {
                                return _owner;
                            }
                            /**
                             * @dev Throws if called by any account other than the owner.
                             */
                            modifier onlyOwner() {
                                require(owner() == _msgSender(), "Ownable: caller is not the owner");
                                _;
                            }
                            /**
                             * @dev Leaves the contract without owner. It will not be possible to call
                             * `onlyOwner` functions anymore. Can only be called by the current owner.
                             *
                             * NOTE: Renouncing ownership will leave the contract without an owner,
                             * thereby removing any functionality that is only available to the owner.
                             */
                            function renounceOwnership() public virtual onlyOwner {
                                emit OwnershipTransferred(_owner, address(0));
                                _owner = address(0);
                            }
                            /**
                             * @dev Transfers ownership of the contract to a new account (`newOwner`).
                             * Can only be called by the current owner.
                             */
                            function transferOwnership(address newOwner) public virtual onlyOwner {
                                require(newOwner != address(0), "Ownable: new owner is the zero address");
                                emit OwnershipTransferred(_owner, newOwner);
                                _owner = newOwner;
                            }
                            uint256[49] private __gap;
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.0 <0.8.0;
                        import "../proxy/Initializable.sol";
                        /**
                         * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
                         *
                         * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
                         * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
                         * they need in their contracts using a combination of `abi.encode` and `keccak256`.
                         *
                         * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
                         * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
                         * ({_hashTypedDataV4}).
                         *
                         * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
                         * the chain id to protect against replay attacks on an eventual fork of the chain.
                         *
                         * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
                         * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
                         *
                         * _Available since v3.4._
                         */
                        abstract contract EIP712Upgradeable is Initializable {
                            /* solhint-disable var-name-mixedcase */
                            bytes32 private _HASHED_NAME;
                            bytes32 private _HASHED_VERSION;
                            bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
                            /* solhint-enable var-name-mixedcase */
                            /**
                             * @dev Initializes the domain separator and parameter caches.
                             *
                             * The meaning of `name` and `version` is specified in
                             * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
                             *
                             * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
                             * - `version`: the current major version of the signing domain.
                             *
                             * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
                             * contract upgrade].
                             */
                            function __EIP712_init(string memory name, string memory version) internal initializer {
                                __EIP712_init_unchained(name, version);
                            }
                            function __EIP712_init_unchained(string memory name, string memory version) internal initializer {
                                bytes32 hashedName = keccak256(bytes(name));
                                bytes32 hashedVersion = keccak256(bytes(version));
                                _HASHED_NAME = hashedName;
                                _HASHED_VERSION = hashedVersion;
                            }
                            /**
                             * @dev Returns the domain separator for the current chain.
                             */
                            function _domainSeparatorV4() internal view returns (bytes32) {
                                return _buildDomainSeparator(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash());
                            }
                            function _buildDomainSeparator(bytes32 typeHash, bytes32 name, bytes32 version) private view returns (bytes32) {
                                return keccak256(
                                    abi.encode(
                                        typeHash,
                                        name,
                                        version,
                                        _getChainId(),
                                        address(this)
                                    )
                                );
                            }
                            /**
                             * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
                             * function returns the hash of the fully encoded EIP712 message for this domain.
                             *
                             * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
                             *
                             * ```solidity
                             * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
                             *     keccak256("Mail(address to,string contents)"),
                             *     mailTo,
                             *     keccak256(bytes(mailContents))
                             * )));
                             * address signer = ECDSA.recover(digest, signature);
                             * ```
                             */
                            function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
                                return keccak256(abi.encodePacked("\\x19\\x01", _domainSeparatorV4(), structHash));
                            }
                            function _getChainId() private view returns (uint256 chainId) {
                                this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                                // solhint-disable-next-line no-inline-assembly
                                assembly {
                                    chainId := chainid()
                                }
                            }
                            /**
                             * @dev The hash of the name parameter for the EIP712 domain.
                             *
                             * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
                             * are a concern.
                             */
                            function _EIP712NameHash() internal virtual view returns (bytes32) {
                                return _HASHED_NAME;
                            }
                            /**
                             * @dev The hash of the version parameter for the EIP712 domain.
                             *
                             * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
                             * are a concern.
                             */
                            function _EIP712VersionHash() internal virtual view returns (bytes32) {
                                return _HASHED_VERSION;
                            }
                            uint256[50] private __gap;
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.0 <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 IERC165Upgradeable {
                            /**
                             * @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
                        pragma solidity >=0.6.0 <0.8.0;
                        /**
                         * @dev Wrappers over Solidity's arithmetic operations with added overflow
                         * checks.
                         *
                         * Arithmetic operations in Solidity wrap on overflow. This can easily result
                         * in bugs, because programmers usually assume that an overflow raises an
                         * error, which is the standard behavior in high level programming languages.
                         * `SafeMath` restores this intuition by reverting the transaction when an
                         * operation overflows.
                         *
                         * Using this library instead of the unchecked operations eliminates an entire
                         * class of bugs, so it's recommended to use it always.
                         */
                        library SafeMathUpgradeable {
                            /**
                             * @dev Returns the addition of two unsigned integers, with an overflow flag.
                             *
                             * _Available since v3.4._
                             */
                            function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                                uint256 c = a + b;
                                if (c < a) return (false, 0);
                                return (true, c);
                            }
                            /**
                             * @dev Returns the substraction of two unsigned integers, with an overflow flag.
                             *
                             * _Available since v3.4._
                             */
                            function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                                if (b > a) return (false, 0);
                                return (true, a - b);
                            }
                            /**
                             * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
                             *
                             * _Available since v3.4._
                             */
                            function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                                // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                                // benefit is lost if 'b' is also tested.
                                // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                                if (a == 0) return (true, 0);
                                uint256 c = a * b;
                                if (c / a != b) return (false, 0);
                                return (true, c);
                            }
                            /**
                             * @dev Returns the division of two unsigned integers, with a division by zero flag.
                             *
                             * _Available since v3.4._
                             */
                            function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                                if (b == 0) return (false, 0);
                                return (true, a / b);
                            }
                            /**
                             * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
                             *
                             * _Available since v3.4._
                             */
                            function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                                if (b == 0) return (false, 0);
                                return (true, a % b);
                            }
                            /**
                             * @dev Returns the addition of two unsigned integers, reverting on
                             * overflow.
                             *
                             * Counterpart to Solidity's `+` operator.
                             *
                             * Requirements:
                             *
                             * - Addition cannot overflow.
                             */
                            function add(uint256 a, uint256 b) internal pure returns (uint256) {
                                uint256 c = a + b;
                                require(c >= a, "SafeMath: addition overflow");
                                return c;
                            }
                            /**
                             * @dev Returns the subtraction of two unsigned integers, reverting on
                             * overflow (when the result is negative).
                             *
                             * Counterpart to Solidity's `-` operator.
                             *
                             * Requirements:
                             *
                             * - Subtraction cannot overflow.
                             */
                            function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                                require(b <= a, "SafeMath: subtraction overflow");
                                return a - b;
                            }
                            /**
                             * @dev Returns the multiplication of two unsigned integers, reverting on
                             * overflow.
                             *
                             * Counterpart to Solidity's `*` operator.
                             *
                             * Requirements:
                             *
                             * - Multiplication cannot overflow.
                             */
                            function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                                if (a == 0) return 0;
                                uint256 c = a * b;
                                require(c / a == b, "SafeMath: multiplication overflow");
                                return c;
                            }
                            /**
                             * @dev Returns the integer division of two unsigned integers, reverting on
                             * division by zero. The result is rounded towards zero.
                             *
                             * Counterpart to Solidity's `/` operator. Note: this function uses a
                             * `revert` opcode (which leaves remaining gas untouched) while Solidity
                             * uses an invalid opcode to revert (consuming all remaining gas).
                             *
                             * Requirements:
                             *
                             * - The divisor cannot be zero.
                             */
                            function div(uint256 a, uint256 b) internal pure returns (uint256) {
                                require(b > 0, "SafeMath: division by zero");
                                return a / b;
                            }
                            /**
                             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                             * reverting when dividing by zero.
                             *
                             * Counterpart to Solidity's `%` operator. This function uses a `revert`
                             * opcode (which leaves remaining gas untouched) while Solidity uses an
                             * invalid opcode to revert (consuming all remaining gas).
                             *
                             * Requirements:
                             *
                             * - The divisor cannot be zero.
                             */
                            function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                                require(b > 0, "SafeMath: modulo by zero");
                                return a % b;
                            }
                            /**
                             * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                             * overflow (when the result is negative).
                             *
                             * CAUTION: This function is deprecated because it requires allocating memory for the error
                             * message unnecessarily. For custom revert reasons use {trySub}.
                             *
                             * Counterpart to Solidity's `-` operator.
                             *
                             * Requirements:
                             *
                             * - Subtraction cannot overflow.
                             */
                            function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                require(b <= a, errorMessage);
                                return a - b;
                            }
                            /**
                             * @dev Returns the integer division of two unsigned integers, reverting with custom message on
                             * division by zero. The result is rounded towards zero.
                             *
                             * CAUTION: This function is deprecated because it requires allocating memory for the error
                             * message unnecessarily. For custom revert reasons use {tryDiv}.
                             *
                             * Counterpart to Solidity's `/` operator. Note: this function uses a
                             * `revert` opcode (which leaves remaining gas untouched) while Solidity
                             * uses an invalid opcode to revert (consuming all remaining gas).
                             *
                             * Requirements:
                             *
                             * - The divisor cannot be zero.
                             */
                            function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                require(b > 0, errorMessage);
                                return a / b;
                            }
                            /**
                             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                             * reverting with custom message when dividing by zero.
                             *
                             * CAUTION: This function is deprecated because it requires allocating memory for the error
                             * message unnecessarily. For custom revert reasons use {tryMod}.
                             *
                             * Counterpart to Solidity's `%` operator. This function uses a `revert`
                             * opcode (which leaves remaining gas untouched) while Solidity uses an
                             * invalid opcode to revert (consuming all remaining gas).
                             *
                             * Requirements:
                             *
                             * - The divisor cannot be zero.
                             */
                            function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                require(b > 0, errorMessage);
                                return a % b;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        // solhint-disable-next-line compiler-version
                        pragma solidity >=0.4.24 <0.8.0;
                        import "../utils/AddressUpgradeable.sol";
                        /**
                         * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
                         * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
                         * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
                         * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
                         *
                         * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
                         * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}.
                         *
                         * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
                         * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
                         */
                        abstract contract Initializable {
                            /**
                             * @dev Indicates that the contract has been initialized.
                             */
                            bool private _initialized;
                            /**
                             * @dev Indicates that the contract is in the process of being initialized.
                             */
                            bool private _initializing;
                            /**
                             * @dev Modifier to protect an initializer function from being invoked twice.
                             */
                            modifier initializer() {
                                require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized");
                                bool isTopLevelCall = !_initializing;
                                if (isTopLevelCall) {
                                    _initializing = true;
                                    _initialized = true;
                                }
                                _;
                                if (isTopLevelCall) {
                                    _initializing = false;
                                }
                            }
                            /// @dev Returns true if and only if the function is running in the constructor
                            function _isConstructor() private view returns (bool) {
                                return !AddressUpgradeable.isContract(address(this));
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.2 <0.8.0;
                        import "../../introspection/IERC165Upgradeable.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 IERC1155Upgradeable is IERC165Upgradeable {
                            /**
                             * @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
                        pragma solidity >=0.6.0 <0.8.0;
                        /**
                         * @dev Interface of the ERC20 standard as defined in the EIP.
                         */
                        interface IERC20Upgradeable {
                            /**
                             * @dev Returns the amount of tokens in existence.
                             */
                            function totalSupply() external view returns (uint256);
                            /**
                             * @dev Returns the amount of tokens owned by `account`.
                             */
                            function balanceOf(address account) external view returns (uint256);
                            /**
                             * @dev Moves `amount` tokens from the caller's account to `recipient`.
                             *
                             * Returns a boolean value indicating whether the operation succeeded.
                             *
                             * Emits a {Transfer} event.
                             */
                            function transfer(address recipient, uint256 amount) external returns (bool);
                            /**
                             * @dev Returns the remaining number of tokens that `spender` will be
                             * allowed to spend on behalf of `owner` through {transferFrom}. This is
                             * zero by default.
                             *
                             * This value changes when {approve} or {transferFrom} are called.
                             */
                            function allowance(address owner, address spender) external view returns (uint256);
                            /**
                             * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                             *
                             * Returns a boolean value indicating whether the operation succeeded.
                             *
                             * IMPORTANT: Beware that changing an allowance with this method brings the risk
                             * that someone may use both the old and the new allowance by unfortunate
                             * transaction ordering. One possible solution to mitigate this race
                             * condition is to first reduce the spender's allowance to 0 and set the
                             * desired value afterwards:
                             * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                             *
                             * Emits an {Approval} event.
                             */
                            function approve(address spender, uint256 amount) external returns (bool);
                            /**
                             * @dev Moves `amount` tokens from `sender` to `recipient` using the
                             * allowance mechanism. `amount` is then deducted from the caller's
                             * allowance.
                             *
                             * Returns a boolean value indicating whether the operation succeeded.
                             *
                             * Emits a {Transfer} event.
                             */
                            function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
                            /**
                             * @dev Emitted when `value` tokens are moved from one account (`from`) to
                             * another (`to`).
                             *
                             * Note that `value` may be zero.
                             */
                            event Transfer(address indexed from, address indexed to, uint256 value);
                            /**
                             * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                             * a call to {approve}. `value` is the new allowance.
                             */
                            event Approval(address indexed owner, address indexed spender, uint256 value);
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.2 <0.8.0;
                        import "../../introspection/IERC165Upgradeable.sol";
                        /**
                         * @dev Required interface of an ERC721 compliant contract.
                         */
                        interface IERC721Upgradeable is IERC165Upgradeable {
                            /**
                             * @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
                        pragma solidity >=0.6.2 <0.8.0;
                        /**
                         * @dev Collection of functions related to the address type
                         */
                        library AddressUpgradeable {
                            /**
                             * @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;
                                // solhint-disable-next-line no-inline-assembly
                                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");
                                // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                                (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");
                                // solhint-disable-next-line avoid-low-level-calls
                                (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");
                                // solhint-disable-next-line avoid-low-level-calls
                                (bool success, bytes memory returndata) = target.staticcall(data);
                                return _verifyCallResult(success, returndata, errorMessage);
                            }
                            function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private 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
                                        // solhint-disable-next-line no-inline-assembly
                                        assembly {
                                            let returndata_size := mload(returndata)
                                            revert(add(32, returndata), returndata_size)
                                        }
                                    } else {
                                        revert(errorMessage);
                                    }
                                }
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.0 <0.8.0;
                        import "../proxy/Initializable.sol";
                        /*
                         * @dev Provides information about the current execution context, including the
                         * sender of the transaction and its data. While these are generally available
                         * via msg.sender and msg.data, they should not be accessed in such a direct
                         * manner, since when dealing with GSN meta-transactions the account sending and
                         * paying for execution may not be the actual sender (as far as an application
                         * is concerned).
                         *
                         * This contract is only required for intermediate, library-like contracts.
                         */
                        abstract contract ContextUpgradeable is Initializable {
                            function __Context_init() internal initializer {
                                __Context_init_unchained();
                            }
                            function __Context_init_unchained() internal initializer {
                            }
                            function _msgSender() internal view virtual returns (address payable) {
                                return msg.sender;
                            }
                            function _msgData() internal view virtual returns (bytes memory) {
                                this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                                return msg.data;
                            }
                            uint256[50] private __gap;
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        pragma abicoder v2;
                        import "@rarible/lib-asset/contracts/LibAsset.sol";
                        interface IAssetMatcher {
                            function matchAssets(
                                LibAsset.AssetType memory leftAssetType,
                                LibAsset.AssetType memory rightAssetType
                            ) external view returns (LibAsset.AssetType memory);
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.9 <0.8.0;
                        pragma abicoder v2;
                        import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
                        interface IERC20TransferProxy {
                            function erc20safeTransferFrom(IERC20Upgradeable token, address from, address to, uint256 value) external;
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.9 <0.8.0;
                        pragma abicoder v2;
                        import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
                        import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol";
                        interface INftTransferProxy {
                            function erc721safeTransferFrom(IERC721Upgradeable token, address from, address to, uint256 tokenId) external;
                            function erc1155safeTransferFrom(IERC1155Upgradeable token, address from, address to, uint256 id, uint256 value, bytes calldata data) external;
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.2 <0.8.0;
                        pragma abicoder v2;
                        import "@rarible/lib-part/contracts/LibPart.sol";
                        interface IRoyaltiesProvider {
                            function getRoyalties(address token, uint tokenId) external returns (LibPart.Part[] memory);
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.9 <0.8.0;
                        pragma abicoder v2;
                        import "@rarible/lib-asset/contracts/LibAsset.sol";
                        interface ITransferProxy {
                            function transfer(LibAsset.Asset calldata asset, address from, address to) external;
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        pragma abicoder v2;
                        import "@rarible/exchange-interfaces/contracts/IAssetMatcher.sol";
                        import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol";
                        import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
                        abstract contract AssetMatcher is Initializable, OwnableUpgradeable {
                            bytes constant EMPTY = "";
                            mapping(bytes4 => address) internal matchers;
                            event MatcherChange(bytes4 indexed assetType, address matcher);
                            function setAssetMatcher(bytes4 assetType, address matcher) external onlyOwner {
                                matchers[assetType] = matcher;
                                emit MatcherChange(assetType, matcher);
                            }
                            function matchAssets(LibAsset.AssetType memory leftAssetType, LibAsset.AssetType memory rightAssetType) internal view returns (LibAsset.AssetType memory) {
                                LibAsset.AssetType memory result = matchAssetOneSide(leftAssetType, rightAssetType);
                                if (result.assetClass == 0) {
                                    return matchAssetOneSide(rightAssetType, leftAssetType);
                                } else {
                                    return result;
                                }
                            }
                            function matchAssetOneSide(LibAsset.AssetType memory leftAssetType, LibAsset.AssetType memory rightAssetType) private view returns (LibAsset.AssetType memory) {
                                bytes4 classLeft = leftAssetType.assetClass;
                                bytes4 classRight = rightAssetType.assetClass;
                                if (classLeft == LibAsset.ETH_ASSET_CLASS) {
                                    if (classRight == LibAsset.ETH_ASSET_CLASS) {
                                        return leftAssetType;
                                    }
                                    return LibAsset.AssetType(0, EMPTY);
                                }
                                if (classLeft == LibAsset.ERC20_ASSET_CLASS) {
                                    if (classRight == LibAsset.ERC20_ASSET_CLASS) {
                                        return simpleMatch(leftAssetType, rightAssetType);
                                    }
                                    return LibAsset.AssetType(0, EMPTY);
                                }
                                if (classLeft == LibAsset.ERC721_ASSET_CLASS) {
                                    if (classRight == LibAsset.ERC721_ASSET_CLASS) {
                                        return simpleMatch(leftAssetType, rightAssetType);
                                    }
                                    return LibAsset.AssetType(0, EMPTY);
                                }
                                if (classLeft == LibAsset.ERC1155_ASSET_CLASS) {
                                    if (classRight == LibAsset.ERC1155_ASSET_CLASS) {
                                        return simpleMatch(leftAssetType, rightAssetType);
                                    }
                                    return LibAsset.AssetType(0, EMPTY);
                                }
                                address matcher = matchers[classLeft];
                                if (matcher != address(0)) {
                                    return IAssetMatcher(matcher).matchAssets(leftAssetType, rightAssetType);
                                }
                                if (classLeft == classRight) {
                                    return simpleMatch(leftAssetType, rightAssetType);
                                }
                                revert("not found IAssetMatcher");
                            }
                            function simpleMatch(LibAsset.AssetType memory leftAssetType, LibAsset.AssetType memory rightAssetType) private pure returns (LibAsset.AssetType memory) {
                                bytes32 leftHash = keccak256(leftAssetType.data);
                                bytes32 rightHash = keccak256(rightAssetType.data);
                                if (leftHash == rightHash) {
                                    return leftAssetType;
                                }
                                return LibAsset.AssetType(0, EMPTY);
                            }
                            uint256[49] private __gap;
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        pragma abicoder v2;
                        import "./ExchangeV2Core.sol";
                        import "@rarible/transfer-manager/contracts/RaribleTransferManager.sol";
                        contract ExchangeV2 is ExchangeV2Core, RaribleTransferManager {
                            function __ExchangeV2_init(
                                address _transferProxy,
                                address _erc20TransferProxy,
                                uint newProtocolFee,
                                address newDefaultFeeReceiver,
                                IRoyaltiesProvider newRoyaltiesProvider
                            ) external initializer {
                                __Context_init_unchained();
                                __Ownable_init_unchained();
                                __TransferExecutor_init_unchained(_transferProxy, _erc20TransferProxy);
                                __RaribleTransferManager_init_unchained(newProtocolFee, newDefaultFeeReceiver, newRoyaltiesProvider);
                                __OrderValidator_init_unchained();
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        pragma abicoder v2;
                        import "./libraries/LibFill.sol";
                        import "./libraries/LibOrderData.sol";
                        import "./libraries/LibDirectTransfer.sol";
                        import "./OrderValidator.sol";
                        import "./AssetMatcher.sol";
                        import "@rarible/transfer-manager/contracts/TransferExecutor.sol";
                        import "@rarible/transfer-manager/contracts/interfaces/ITransferManager.sol";
                        import "@rarible/transfer-manager/contracts/lib/LibDeal.sol";
                        abstract contract ExchangeV2Core is Initializable, OwnableUpgradeable, AssetMatcher, TransferExecutor, OrderValidator, ITransferManager {
                            using SafeMathUpgradeable for uint;
                            using LibTransfer for address;
                            uint256 private constant UINT256_MAX = type(uint256).max;
                            //state of the orders
                            mapping(bytes32 => uint) public fills;
                            //events
                            event Cancel(bytes32 hash);
                            event Match(bytes32 leftHash, bytes32 rightHash, uint newLeftFill, uint newRightFill);
                            function cancel(LibOrder.Order memory order) external {
                                require(_msgSender() == order.maker, "not a maker");
                                require(order.salt != 0, "0 salt can't be used");
                                bytes32 orderKeyHash = LibOrder.hashKey(order);
                                fills[orderKeyHash] = UINT256_MAX;
                                emit Cancel(orderKeyHash);
                            }
                            /**
                             * @dev function, generate sellOrder and buyOrder from parameters and call validateAndMatch() for purchase transaction
                         
                            */
                            function directPurchase(
                                LibDirectTransfer.Purchase calldata direct
                            ) external payable{
                                LibAsset.AssetType memory paymentAssetType = getPaymentAssetType(direct.paymentToken);
                                        
                                LibOrder.Order memory sellOrder = LibOrder.Order(
                                    direct.sellOrderMaker,
                                    LibAsset.Asset(
                                        LibAsset.AssetType(
                                            direct.nftAssetClass,
                                            direct.nftData
                                        ),
                                        direct.sellOrderNftAmount
                                    ),
                                    address(0),
                                    LibAsset.Asset(
                                        paymentAssetType,
                                        direct.sellOrderPaymentAmount
                                    ),
                                    direct.sellOrderSalt,
                                    direct.sellOrderStart,
                                    direct.sellOrderEnd,
                                    direct.sellOrderDataType,
                                    direct.sellOrderData
                                );
                                LibOrder.Order memory buyOrder = LibOrder.Order(
                                    address(0),
                                    LibAsset.Asset(
                                        paymentAssetType,
                                        direct.buyOrderPaymentAmount
                                    ),
                                    address(0),
                                    LibAsset.Asset(
                                        LibAsset.AssetType(
                                            direct.nftAssetClass,
                                            direct.nftData
                                        ),
                                        direct.buyOrderNftAmount
                                    ),
                                    0,
                                    0,
                                    0,
                                    direct.sellOrderDataType,
                                    direct.buyOrderData
                                );
                                validateFull(sellOrder, direct.sellOrderSignature);
                                matchAndTransfer(sellOrder, buyOrder);
                            }
                            /**
                             * @dev function, generate sellOrder and buyOrder from parameters and call validateAndMatch() for accept bid transaction
                             * @param direct struct with parameters for accept bid operation
                             */
                            function directAcceptBid(
                                LibDirectTransfer.AcceptBid calldata direct
                            ) external payable {
                                LibAsset.AssetType memory paymentAssetType = getPaymentAssetType(direct.paymentToken);
                                LibOrder.Order memory buyOrder = LibOrder.Order(
                                    direct.bidMaker,
                                    LibAsset.Asset(
                                        paymentAssetType,
                                        direct.bidPaymentAmount
                                    ),
                                    address(0),
                                    LibAsset.Asset(
                                        LibAsset.AssetType(
                                            direct.nftAssetClass,
                                            direct.nftData
                                        ),
                                        direct.bidNftAmount
                                    ),
                                    direct.bidSalt,
                                    direct.bidStart,
                                    direct.bidEnd,
                                    direct.bidDataType,
                                    direct.bidData
                                );
                                LibOrder.Order memory sellOrder = LibOrder.Order(
                                    address(0),
                                    LibAsset.Asset(
                                        LibAsset.AssetType(
                                            direct.nftAssetClass,
                                            direct.nftData
                                        ),
                                        direct.sellOrderNftAmount
                                    ),
                                    address(0),
                                    LibAsset.Asset(
                                        paymentAssetType,
                                        direct.sellOrderPaymentAmount
                                    ),
                                    0,
                                    0,
                                    0,
                                    direct.bidDataType,
                                    direct.sellOrderData
                                );
                                validateFull(buyOrder, direct.bidSignature);
                                matchAndTransfer(sellOrder, buyOrder);
                            }
                            function matchOrders(
                                LibOrder.Order memory orderLeft,
                                bytes memory signatureLeft,
                                LibOrder.Order memory orderRight,
                                bytes memory signatureRight
                            ) external payable {
                                validateOrders(orderLeft, signatureLeft, orderRight, signatureRight);
                                matchAndTransfer(orderLeft, orderRight);
                            }
                            /**
                              * @dev function, validate orders
                              * @param orderLeft left order
                              * @param signatureLeft order left signature
                              * @param orderRight right order
                              * @param signatureRight order right signature
                              */
                            function validateOrders(LibOrder.Order memory orderLeft, bytes memory signatureLeft, LibOrder.Order memory orderRight, bytes memory signatureRight) internal view {
                                validateFull(orderLeft, signatureLeft);
                                validateFull(orderRight, signatureRight);
                                if (orderLeft.taker != address(0)) {
                                    if (orderRight.maker != address(0))
                                        require(orderRight.maker == orderLeft.taker, "leftOrder.taker verification failed");
                                }
                                if (orderRight.taker != address(0)) {
                                    if (orderLeft.maker != address(0))
                                        require(orderRight.taker == orderLeft.maker, "rightOrder.taker verification failed");
                                }
                            }
                            /**
                                @notice matches valid orders and transfers their assets
                                @param orderLeft the left order of the match
                                @param orderRight the right order of the match
                            */
                            function matchAndTransfer(LibOrder.Order memory orderLeft, LibOrder.Order memory orderRight) internal {
                                (LibAsset.AssetType memory makeMatch, LibAsset.AssetType memory takeMatch) = matchAssets(orderLeft, orderRight);
                                (LibOrderData.GenericOrderData memory leftOrderData, LibOrderData.GenericOrderData memory rightOrderData, LibFill.FillResult memory newFill) =
                                    parseOrdersSetFillEmitMatch(orderLeft, orderRight);
                                (uint totalMakeValue, uint totalTakeValue) = doTransfers(
                                    LibDeal.DealSide({
                                        asset: LibAsset.Asset({
                                            assetType: makeMatch,
                                            value: newFill.leftValue
                                        }),
                                        payouts: leftOrderData.payouts,
                                        originFees: leftOrderData.originFees,
                                        proxy: proxies[makeMatch.assetClass],
                                        from: orderLeft.maker,
                                        protocolFeeEnabled: leftOrderData.protocolFeeEnabled
                                    }), 
                                    LibDeal.DealSide({
                                        asset: LibAsset.Asset( 
                                            takeMatch,
                                            newFill.rightValue
                                        ),
                                        payouts: rightOrderData.payouts,
                                        originFees: rightOrderData.originFees,
                                        proxy: proxies[takeMatch.assetClass],
                                        from: orderRight.maker,
                                        protocolFeeEnabled: rightOrderData.protocolFeeEnabled
                                    }),
                                    LibFeeSide.getFeeSide(makeMatch.assetClass, takeMatch.assetClass)
                                );
                                if (makeMatch.assetClass == LibAsset.ETH_ASSET_CLASS) {
                                    require(takeMatch.assetClass != LibAsset.ETH_ASSET_CLASS);
                                    require(msg.value >= totalMakeValue, "not enough eth");
                                    if (msg.value > totalMakeValue) {
                                        address(msg.sender).transferEth(msg.value.sub(totalMakeValue));
                                    }
                                } else if (takeMatch.assetClass == LibAsset.ETH_ASSET_CLASS) {
                                    require(msg.value >= totalTakeValue, "not enough eth");
                                    if (msg.value > totalTakeValue) {
                                        address(msg.sender).transferEth(msg.value.sub(totalTakeValue));
                                    }
                                }
                            }
                            function parseOrdersSetFillEmitMatch(
                                LibOrder.Order memory orderLeft,
                                LibOrder.Order memory orderRight
                            ) internal returns (LibOrderData.GenericOrderData memory leftOrderData, LibOrderData.GenericOrderData memory rightOrderData, LibFill.FillResult memory newFill) {
                                bytes32 leftOrderKeyHash = LibOrder.hashKey(orderLeft);
                                bytes32 rightOrderKeyHash = LibOrder.hashKey(orderRight);
                                address msgSender = _msgSender();
                                if (orderLeft.maker == address(0)) {
                                    orderLeft.maker = msgSender;
                                }
                                if (orderRight.maker == address(0)) {
                                    orderRight.maker = msgSender;
                                }
                                leftOrderData = LibOrderData.parse(orderLeft);
                                rightOrderData = LibOrderData.parse(orderRight);
                                newFill = setFillEmitMatch(
                                    orderLeft,
                                    orderRight,
                                    leftOrderKeyHash,
                                    rightOrderKeyHash,
                                    leftOrderData.isMakeFill,
                                    rightOrderData.isMakeFill
                                );
                            }
                            /**
                                @notice calculates fills for the matched orders and set them in "fills" mapping
                                @param orderLeft left order of the match
                                @param orderRight right order of the match
                                @param leftMakeFill true if the left orders uses make-side fills, false otherwise
                                @param rightMakeFill true if the right orders uses make-side fills, false otherwise
                                @return returns change in orders' fills by the match 
                            */
                            function setFillEmitMatch(
                                LibOrder.Order memory orderLeft,
                                LibOrder.Order memory orderRight,
                                bytes32 leftOrderKeyHash,
                                bytes32 rightOrderKeyHash,
                                bool leftMakeFill,
                                bool rightMakeFill
                            ) internal returns (LibFill.FillResult memory) {
                                uint leftOrderFill = getOrderFill(orderLeft.salt, leftOrderKeyHash);
                                uint rightOrderFill = getOrderFill(orderRight.salt, rightOrderKeyHash);
                                LibFill.FillResult memory newFill = LibFill.fillOrder(orderLeft, orderRight, leftOrderFill, rightOrderFill, leftMakeFill, rightMakeFill);
                                if (orderLeft.makeAsset.value != 0 || orderRight.takeAsset.value != 0) {
                                    require(newFill.leftValue > 0, "nothing to fill");
                                }
                                if (orderLeft.takeAsset.value != 0 || orderRight.makeAsset.value != 0) {
                                    require(newFill.rightValue > 0, "nothing to fill");
                                }
                                if (orderLeft.salt != 0) {
                                    if (leftMakeFill) {
                                        fills[leftOrderKeyHash] = leftOrderFill.add(newFill.leftValue);
                                    } else {
                                        fills[leftOrderKeyHash] = leftOrderFill.add(newFill.rightValue);
                                    }
                                }
                                if (orderRight.salt != 0) {
                                    if (rightMakeFill) {
                                        fills[rightOrderKeyHash] = rightOrderFill.add(newFill.rightValue);
                                    } else {
                                        fills[rightOrderKeyHash] = rightOrderFill.add(newFill.leftValue);
                                    }
                                }
                                emit Match(leftOrderKeyHash, rightOrderKeyHash, newFill.rightValue, newFill.leftValue);
                                return newFill;
                            }
                            function getOrderFill(uint salt, bytes32 hash) internal view returns (uint fill) {
                                if (salt == 0) {
                                    fill = 0;
                                } else {
                                    fill = fills[hash];
                                }
                            }
                            function matchAssets(LibOrder.Order memory orderLeft, LibOrder.Order memory orderRight) internal view returns (LibAsset.AssetType memory makeMatch, LibAsset.AssetType memory takeMatch) {
                                makeMatch = matchAssets(orderLeft.makeAsset.assetType, orderRight.takeAsset.assetType);
                                require(makeMatch.assetClass != 0, "assets don't match");
                                takeMatch = matchAssets(orderLeft.takeAsset.assetType, orderRight.makeAsset.assetType);
                                require(takeMatch.assetClass != 0, "assets don't match");
                            }
                            function validateFull(LibOrder.Order memory order, bytes memory signature) internal view {
                                LibOrder.validateOrderTime(order);
                                validate(order, signature);
                            }
                            function getPaymentAssetType(address token) internal pure returns(LibAsset.AssetType memory){
                                LibAsset.AssetType memory result;
                                if(token == address(0)) {
                                    result.assetClass = LibAsset.ETH_ASSET_CLASS;
                                } else {
                                    result.assetClass = LibAsset.ERC20_ASSET_CLASS;
                                    result.data = abi.encode(token);
                                }
                                return result;
                            }
                            uint256[49] private __gap;
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        import "./libraries/LibOrder.sol";
                        import "@rarible/lib-signature/contracts/IERC1271.sol";
                        import "@rarible/lib-signature/contracts/LibSignature.sol";
                        import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
                        import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
                        import "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol";
                        abstract contract OrderValidator is Initializable, ContextUpgradeable, EIP712Upgradeable {
                            using LibSignature for bytes32;
                            using AddressUpgradeable for address;
                            
                            bytes4 constant internal MAGICVALUE = 0x1626ba7e;
                            function __OrderValidator_init_unchained() internal initializer {
                                __EIP712_init_unchained("Exchange", "2");
                            }
                            function validate(LibOrder.Order memory order, bytes memory signature) internal view {
                                if (order.salt == 0) {
                                    if (order.maker != address(0)) {
                                        require(_msgSender() == order.maker, "maker is not tx sender");
                                    }
                                } else {
                                    if (_msgSender() != order.maker) {
                                        bytes32 hash = LibOrder.hash(order);
                                        // if maker is contract checking ERC1271 signature
                                        if (order.maker.isContract()) {
                                            require(
                                                IERC1271(order.maker).isValidSignature(_hashTypedDataV4(hash), signature) == MAGICVALUE,
                                                "contract order signature verification error"
                                            );
                                        } else {
                                            // if maker is not contract then checking ECDSA signature
                                            if (_hashTypedDataV4(hash).recover(signature) != order.maker) {
                                                revert("order signature verification error");
                                            } else {
                                                require (order.maker != address(0), "no maker");
                                            }
                                        }
                                    }
                                }
                            }
                            uint256[50] private __gap;
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        import "@rarible/lib-asset/contracts/LibAsset.sol";
                        library LibDirectTransfer { //LibDirectTransfers
                            /*All buy parameters need for create buyOrder and sellOrder*/
                            struct Purchase {
                                address sellOrderMaker; //
                                uint256 sellOrderNftAmount;
                                bytes4 nftAssetClass;
                                bytes nftData;
                                uint256 sellOrderPaymentAmount;
                                address paymentToken;
                                uint256 sellOrderSalt;
                                uint sellOrderStart;
                                uint sellOrderEnd;
                                bytes4 sellOrderDataType;
                                bytes sellOrderData;
                                bytes sellOrderSignature;
                                uint256 buyOrderPaymentAmount;
                                uint256 buyOrderNftAmount;
                                bytes buyOrderData;
                            }
                            /*All accept bid parameters need for create buyOrder and sellOrder*/
                            struct AcceptBid {
                                address bidMaker; //
                                uint256 bidNftAmount;
                                bytes4 nftAssetClass;
                                bytes nftData;
                                uint256 bidPaymentAmount;
                                address paymentToken;
                                uint256 bidSalt;
                                uint bidStart;
                                uint bidEnd;
                                bytes4 bidDataType;
                                bytes bidData;
                                bytes bidSignature;
                                uint256 sellOrderPaymentAmount;
                                uint256 sellOrderNftAmount;
                                bytes sellOrderData;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        import "./LibOrder.sol";
                        library LibFill {
                            struct FillResult {
                                uint leftValue;
                                uint rightValue;
                            }
                            struct IsMakeFill {
                                bool leftMake;
                                bool rightMake;
                            }
                            /**
                             * @dev Should return filled values
                             * @param leftOrder left order
                             * @param rightOrder right order
                             * @param leftOrderFill current fill of the left order (0 if order is unfilled)
                             * @param rightOrderFill current fill of the right order (0 if order is unfilled)
                             * @param leftIsMakeFill true if left orders fill is calculated from the make side, false if from the take side
                             * @param rightIsMakeFill true if right orders fill is calculated from the make side, false if from the take side
                             * @return tuple representing fill of both assets
                             */
                            function fillOrder(LibOrder.Order memory leftOrder, LibOrder.Order memory rightOrder, uint leftOrderFill, uint rightOrderFill, bool leftIsMakeFill, bool rightIsMakeFill) internal pure returns (FillResult memory) {
                                (uint leftMakeValue, uint leftTakeValue) = LibOrder.calculateRemaining(leftOrder, leftOrderFill, leftIsMakeFill);
                                (uint rightMakeValue, uint rightTakeValue) = LibOrder.calculateRemaining(rightOrder, rightOrderFill, rightIsMakeFill);
                                //We have 3 cases here:
                                if (rightTakeValue > leftMakeValue || (rightTakeValue == leftMakeValue && leftMakeValue == 0)) { //1nd: left order should be fully filled
                                    return fillLeft(leftMakeValue, leftTakeValue, rightOrder.makeAsset.value, rightOrder.takeAsset.value);
                                }//2st: right order should be fully filled or 3d: both should be fully filled if required values are the same
                                return fillRight(leftOrder.makeAsset.value, leftOrder.takeAsset.value, rightMakeValue, rightTakeValue);
                            }
                            function fillRight(uint leftMakeValue, uint leftTakeValue, uint rightMakeValue, uint rightTakeValue) internal pure returns (FillResult memory result) {
                                uint makerValue = LibMath.safeGetPartialAmountFloor(rightTakeValue, leftMakeValue, leftTakeValue);
                                require(makerValue <= rightMakeValue, "fillRight: unable to fill");
                                return FillResult(rightTakeValue, makerValue);
                            }
                            function fillLeft(uint leftMakeValue, uint leftTakeValue, uint rightMakeValue, uint rightTakeValue) internal pure returns (FillResult memory result) {
                                uint rightTake = LibMath.safeGetPartialAmountFloor(leftTakeValue, rightMakeValue, rightTakeValue);
                                require(rightTake <= leftMakeValue, "fillLeft: unable to fill");
                                return FillResult(leftMakeValue, leftTakeValue);
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
                        library LibMath {
                            using SafeMathUpgradeable for uint;
                            /// @dev Calculates partial value given a numerator and denominator rounded down.
                            ///      Reverts if rounding error is >= 0.1%
                            /// @param numerator Numerator.
                            /// @param denominator Denominator.
                            /// @param target Value to calculate partial of.
                            /// @return partialAmount value of target rounded down.
                            function safeGetPartialAmountFloor(
                                uint256 numerator,
                                uint256 denominator,
                                uint256 target
                            ) internal pure returns (uint256 partialAmount) {
                                if (isRoundingErrorFloor(numerator, denominator, target)) {
                                    revert("rounding error");
                                }
                                partialAmount = numerator.mul(target).div(denominator);
                            }
                            /// @dev Checks if rounding error >= 0.1% when rounding down.
                            /// @param numerator Numerator.
                            /// @param denominator Denominator.
                            /// @param target Value to multiply with numerator/denominator.
                            /// @return isError Rounding error is present.
                            function isRoundingErrorFloor(
                                uint256 numerator,
                                uint256 denominator,
                                uint256 target
                            ) internal pure returns (bool isError) {
                                if (denominator == 0) {
                                    revert("division by zero");
                                }
                                // The absolute rounding error is the difference between the rounded
                                // value and the ideal value. The relative rounding error is the
                                // absolute rounding error divided by the absolute value of the
                                // ideal value. This is undefined when the ideal value is zero.
                                //
                                // The ideal value is `numerator * target / denominator`.
                                // Let's call `numerator * target % denominator` the remainder.
                                // The absolute error is `remainder / denominator`.
                                //
                                // When the ideal value is zero, we require the absolute error to
                                // be zero. Fortunately, this is always the case. The ideal value is
                                // zero iff `numerator == 0` and/or `target == 0`. In this case the
                                // remainder and absolute error are also zero.
                                if (target == 0 || numerator == 0) {
                                    return false;
                                }
                                // Otherwise, we want the relative rounding error to be strictly
                                // less than 0.1%.
                                // The relative error is `remainder / (numerator * target)`.
                                // We want the relative error less than 1 / 1000:
                                //        remainder / (numerator * target)  <  1 / 1000
                                // or equivalently:
                                //        1000 * remainder  <  numerator * target
                                // so we have a rounding error iff:
                                //        1000 * remainder  >=  numerator * target
                                uint256 remainder = mulmod(
                                    target,
                                    numerator,
                                    denominator
                                );
                                isError = remainder.mul(1000) >= numerator.mul(target);
                            }
                            function safeGetPartialAmountCeil(
                                uint256 numerator,
                                uint256 denominator,
                                uint256 target
                            ) internal pure returns (uint256 partialAmount) {
                                if (isRoundingErrorCeil(numerator, denominator, target)) {
                                    revert("rounding error");
                                }
                                partialAmount = numerator.mul(target).add(denominator.sub(1)).div(denominator);
                            }
                            /// @dev Checks if rounding error >= 0.1% when rounding up.
                            /// @param numerator Numerator.
                            /// @param denominator Denominator.
                            /// @param target Value to multiply with numerator/denominator.
                            /// @return isError Rounding error is present.
                            function isRoundingErrorCeil(
                                uint256 numerator,
                                uint256 denominator,
                                uint256 target
                            ) internal pure returns (bool isError) {
                                if (denominator == 0) {
                                    revert("division by zero");
                                }
                                // See the comments in `isRoundingError`.
                                if (target == 0 || numerator == 0) {
                                    // When either is zero, the ideal value and rounded value are zero
                                    // and there is no rounding error. (Although the relative error
                                    // is undefined.)
                                    return false;
                                }
                                // Compute remainder as before
                                uint256 remainder = mulmod(
                                    target,
                                    numerator,
                                    denominator
                                );
                                remainder = denominator.sub(remainder) % denominator;
                                isError = remainder.mul(1000) >= numerator.mul(target);
                                return isError;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        import "@rarible/lib-asset/contracts/LibAsset.sol";
                        import "./LibMath.sol";
                        import "./LibOrderDataV3.sol";
                        import "./LibOrderDataV2.sol";
                        import "./LibOrderDataV1.sol";
                        library LibOrder {
                            using SafeMathUpgradeable for uint;
                            bytes32 constant ORDER_TYPEHASH = keccak256(
                                "Order(address maker,Asset makeAsset,address taker,Asset takeAsset,uint256 salt,uint256 start,uint256 end,bytes4 dataType,bytes data)Asset(AssetType assetType,uint256 value)AssetType(bytes4 assetClass,bytes data)"
                            );
                            bytes4 constant DEFAULT_ORDER_TYPE = 0xffffffff;
                            struct Order {
                                address maker;
                                LibAsset.Asset makeAsset;
                                address taker;
                                LibAsset.Asset takeAsset;
                                uint salt;
                                uint start;
                                uint end;
                                bytes4 dataType;
                                bytes data;
                            }
                            /**
                             * @dev Calculate remaining make and take values of the order (after partial filling real make and take decrease)
                             * @param order initial order to calculate remaining values for
                             * @param fill current fill of the left order (0 if order is unfilled)
                             * @param isMakeFill true if order fill is calculated from the make side, false if from the take side
                             * @return makeValue remaining make value of the order. if fill = 0 then it's order's make value
                             * @return takeValue remaining take value of the order. if fill = 0 then it's order's take value
                             */
                            function calculateRemaining(Order memory order, uint fill, bool isMakeFill) internal pure returns (uint makeValue, uint takeValue) {
                                if (isMakeFill) {
                                    makeValue = order.makeAsset.value.sub(fill);
                                    takeValue = LibMath.safeGetPartialAmountFloor(order.takeAsset.value, order.makeAsset.value, makeValue);
                                } else {
                                    takeValue = order.takeAsset.value.sub(fill);
                                    makeValue = LibMath.safeGetPartialAmountFloor(order.makeAsset.value, order.takeAsset.value, takeValue); 
                                } 
                            }
                            function hashKey(Order memory order) internal pure returns (bytes32) {
                                if (order.dataType == LibOrderDataV1.V1 || order.dataType == DEFAULT_ORDER_TYPE) {
                                    return keccak256(abi.encode(
                                        order.maker,
                                        LibAsset.hash(order.makeAsset.assetType),
                                        LibAsset.hash(order.takeAsset.assetType),
                                        order.salt
                                    ));
                                } else {
                                    //order.data is in hash for V2, V3 and all new order
                                    return keccak256(abi.encode(
                                        order.maker,
                                        LibAsset.hash(order.makeAsset.assetType),
                                        LibAsset.hash(order.takeAsset.assetType),
                                        order.salt,
                                        order.data
                                    ));
                                }
                            }
                            function hash(Order memory order) internal pure returns (bytes32) {
                                return keccak256(abi.encode(
                                        ORDER_TYPEHASH,
                                        order.maker,
                                        LibAsset.hash(order.makeAsset),
                                        order.taker,
                                        LibAsset.hash(order.takeAsset),
                                        order.salt,
                                        order.start,
                                        order.end,
                                        order.dataType,
                                        keccak256(order.data)
                                    ));
                            }
                            function validateOrderTime(LibOrder.Order memory order) internal view {
                                require(order.start == 0 || order.start < block.timestamp, "Order start validation failed");
                                require(order.end == 0 || order.end > block.timestamp, "Order end validation failed");
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        pragma abicoder v2;
                        import "./LibOrder.sol";
                        library LibOrderData {
                            struct GenericOrderData {
                                LibPart.Part[] payouts;
                                LibPart.Part[] originFees;
                                bool isMakeFill;
                                bool protocolFeeEnabled;
                            } 
                            function parse(LibOrder.Order memory order) pure internal returns (GenericOrderData memory dataOrder) {
                                dataOrder.protocolFeeEnabled = false;
                                if (order.dataType == LibOrderDataV1.V1) {
                                    LibOrderDataV1.DataV1 memory data = abi.decode(order.data, (LibOrderDataV1.DataV1));
                                    dataOrder.payouts = data.payouts;
                                    dataOrder.originFees = data.originFees;
                                } else if (order.dataType == LibOrderDataV2.V2) {
                                    LibOrderDataV2.DataV2 memory data = abi.decode(order.data, (LibOrderDataV2.DataV2));
                                    dataOrder.payouts = data.payouts;
                                    dataOrder.originFees = data.originFees;
                                    dataOrder.isMakeFill = data.isMakeFill;
                                } else if (order.dataType == LibOrderDataV3.V3) {
                                    LibOrderDataV3.DataV3 memory data = abi.decode(order.data, (LibOrderDataV3.DataV3));
                                    dataOrder.payouts = data.payouts;
                                    dataOrder.originFees = data.originFees;
                                    dataOrder.isMakeFill = data.isMakeFill;
                                    dataOrder.protocolFeeEnabled = true;
                                } else if (order.dataType == 0xffffffff) {
                                } else {
                                    revert("Unknown Order data type");
                                }
                                if (dataOrder.payouts.length == 0) {
                                    dataOrder.payouts = payoutSet(order.maker);
                                }
                            }
                            function payoutSet(address orderAddress) pure internal returns (LibPart.Part[] memory) {
                                LibPart.Part[] memory payout = new LibPart.Part[](1);
                                payout[0].account = payable(orderAddress);
                                payout[0].value = 10000;
                                return payout;
                            }
                            function parseOriginFeeData(uint dataFirst, uint dataSecond) internal pure returns(LibPart.Part[] memory) {
                                LibPart.Part[] memory originFee;
                                if (dataFirst > 0 && dataSecond > 0){
                                    originFee = new LibPart.Part[](2);
                                    originFee[0] = uintToLibPart(dataFirst);
                                    originFee[1] = uintToLibPart(dataSecond);
                                }
                                if (dataFirst > 0 && dataSecond == 0) {
                                    originFee = new LibPart.Part[](1);
                                    originFee[0] = uintToLibPart(dataFirst);
                                }
                                if (dataFirst == 0 && dataSecond > 0) {
                                    originFee = new LibPart.Part[](1);
                                    originFee[0] = uintToLibPart(dataSecond);
                                }
                                return originFee;
                            }
                            function parsePayouts(uint data) internal pure returns(LibPart.Part[] memory) {
                                LibPart.Part[] memory payouts;
                                if (data > 0) {
                                    payouts = new LibPart.Part[](1);
                                    payouts[0] = uintToLibPart(data);
                                }
                                return payouts;
                            }
                            /**
                                @notice converts uint to LibPart.Part
                                @param data address and value encoded in uint (first 12 bytes )
                                @return result LibPart.Part 
                             */
                            function uintToLibPart(uint data) internal pure returns(LibPart.Part memory result) {
                                if (data > 0){
                                    result.account = payable(address(data));
                                    result.value = uint96(data >> 160);
                                }
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        pragma abicoder v2;
                        import "@rarible/lib-part/contracts/LibPart.sol";
                        library LibOrderDataV1 {
                            bytes4 constant public V1 = bytes4(keccak256("V1"));
                            struct DataV1 {
                                LibPart.Part[] payouts;
                                LibPart.Part[] originFees;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        pragma abicoder v2;
                        import "@rarible/lib-part/contracts/LibPart.sol";
                        library LibOrderDataV2 {
                            bytes4 constant public V2 = bytes4(keccak256("V2"));
                            struct DataV2 {
                                LibPart.Part[] payouts;
                                LibPart.Part[] originFees;
                                bool isMakeFill;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        pragma abicoder v2;
                        import "@rarible/lib-part/contracts/LibPart.sol";
                        library LibOrderDataV3 {
                            bytes4 constant public V3 = bytes4(keccak256("V3"));
                            struct DataV3 {
                                LibPart.Part[] payouts;
                                LibPart.Part[] originFees;
                                bool isMakeFill;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.2 <0.8.0;
                        import "@rarible/lib-part/contracts/LibPart.sol";
                        library LibERC1155LazyMint {
                            bytes4 constant public ERC1155_LAZY_ASSET_CLASS = bytes4(keccak256("ERC1155_LAZY"));
                            bytes4 constant _INTERFACE_ID_MINT_AND_TRANSFER = 0x6db15a0f;
                            struct Mint1155Data {
                                uint tokenId;
                                string tokenURI;
                                uint supply;
                                LibPart.Part[] creators;
                                LibPart.Part[] royalties;
                                bytes[] signatures;
                            }
                            bytes32 public constant MINT_AND_TRANSFER_TYPEHASH = keccak256("Mint1155(uint256 tokenId,uint256 supply,string tokenURI,Part[] creators,Part[] royalties)Part(address account,uint96 value)");
                            function hash(Mint1155Data memory data) internal pure returns (bytes32) {
                                bytes32[] memory royaltiesBytes = new bytes32[](data.royalties.length);
                                for (uint i = 0; i < data.royalties.length; ++i) {
                                    royaltiesBytes[i] = LibPart.hash(data.royalties[i]);
                                }
                                bytes32[] memory creatorsBytes = new bytes32[](data.creators.length);
                                for (uint i = 0; i < data.creators.length; ++i) {
                                    creatorsBytes[i] = LibPart.hash(data.creators[i]);
                                }
                                return keccak256(abi.encode(
                                        MINT_AND_TRANSFER_TYPEHASH,
                                        data.tokenId,
                                        data.supply,
                                        keccak256(bytes(data.tokenURI)),
                                        keccak256(abi.encodePacked(creatorsBytes)),
                                        keccak256(abi.encodePacked(royaltiesBytes))
                                    ));
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.2 <0.8.0;
                        import "@rarible/lib-part/contracts/LibPart.sol";
                        library LibERC721LazyMint {
                            bytes4 constant public ERC721_LAZY_ASSET_CLASS = bytes4(keccak256("ERC721_LAZY"));
                            bytes4 constant _INTERFACE_ID_MINT_AND_TRANSFER = 0x8486f69f;
                            struct Mint721Data {
                                uint tokenId;
                                string tokenURI;
                                LibPart.Part[] creators;
                                LibPart.Part[] royalties;
                                bytes[] signatures;
                            }
                            bytes32 public constant MINT_AND_TRANSFER_TYPEHASH = keccak256("Mint721(uint256 tokenId,string tokenURI,Part[] creators,Part[] royalties)Part(address account,uint96 value)");
                            function hash(Mint721Data memory data) internal pure returns (bytes32) {
                                bytes32[] memory royaltiesBytes = new bytes32[](data.royalties.length);
                                for (uint i = 0; i < data.royalties.length; ++i) {
                                    royaltiesBytes[i] = LibPart.hash(data.royalties[i]);
                                }
                                bytes32[] memory creatorsBytes = new bytes32[](data.creators.length);
                                for (uint i = 0; i < data.creators.length; ++i) {
                                    creatorsBytes[i] = LibPart.hash(data.creators[i]);
                                }
                                return keccak256(abi.encode(
                                        MINT_AND_TRANSFER_TYPEHASH,
                                        data.tokenId,
                                        keccak256(bytes(data.tokenURI)),
                                        keccak256(abi.encodePacked(creatorsBytes)),
                                        keccak256(abi.encodePacked(royaltiesBytes))
                                    ));
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        library LibAsset {
                            bytes4 constant public ETH_ASSET_CLASS = bytes4(keccak256("ETH"));
                            bytes4 constant public ERC20_ASSET_CLASS = bytes4(keccak256("ERC20"));
                            bytes4 constant public ERC721_ASSET_CLASS = bytes4(keccak256("ERC721"));
                            bytes4 constant public ERC1155_ASSET_CLASS = bytes4(keccak256("ERC1155"));
                            bytes4 constant public COLLECTION = bytes4(keccak256("COLLECTION"));
                            bytes4 constant public CRYPTO_PUNKS = bytes4(keccak256("CRYPTO_PUNKS"));
                            bytes32 constant ASSET_TYPE_TYPEHASH = keccak256(
                                "AssetType(bytes4 assetClass,bytes data)"
                            );
                            bytes32 constant ASSET_TYPEHASH = keccak256(
                                "Asset(AssetType assetType,uint256 value)AssetType(bytes4 assetClass,bytes data)"
                            );
                            struct AssetType {
                                bytes4 assetClass;
                                bytes data;
                            }
                            struct Asset {
                                AssetType assetType;
                                uint value;
                            }
                            function hash(AssetType memory assetType) internal pure returns (bytes32) {
                                return keccak256(abi.encode(
                                        ASSET_TYPE_TYPEHASH,
                                        assetType.assetClass,
                                        keccak256(assetType.data)
                                    ));
                            }
                            function hash(Asset memory asset) internal pure returns (bytes32) {
                                return keccak256(abi.encode(
                                        ASSET_TYPEHASH,
                                        hash(asset.assetType),
                                        asset.value
                                    ));
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
                        library BpLibrary {
                            using SafeMathUpgradeable for uint;
                            function bp(uint value, uint bpValue) internal pure returns (uint) {
                                return value.mul(bpValue).div(10000);
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.2 <0.8.0;
                        library LibPart {
                            bytes32 public constant TYPE_HASH = keccak256("Part(address account,uint96 value)");
                            struct Part {
                                address payable account;
                                uint96 value;
                            }
                            function hash(Part memory part) internal pure returns (bytes32) {
                                return keccak256(abi.encode(TYPE_HASH, part.account, part.value));
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        interface IERC1271 {
                            /**
                             * @dev Should return whether the signature provided is valid for the provided data
                             * @param _hash Hash of the data signed on the behalf of address(this)
                             * @param _signature Signature byte array associated with _data
                             *
                             * MUST return the bytes4 magic value 0x1626ba7e when function passes.
                             * MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5)
                             * MUST allow external calls
                             */
                            function isValidSignature(bytes32 _hash, bytes calldata _signature) virtual external view returns (bytes4 magicValue);
                        }// SPDX-License-Identifier: MIT
                        pragma solidity ^0.7.0;
                        library LibSignature {
                            /**
                             * @dev Returns the address that signed a hashed message (`hash`) with
                             * `signature`. This address can then be used for verification purposes.
                             *
                             * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                             * this function rejects them by requiring the `s` value to be in the lower
                             * half order, and the `v` value to be either 27 or 28.
                             *
                             * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                             * verification to be secure: it is possible to craft signatures that
                             * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                             * this is by receiving a hash of the original message (which may otherwise
                             * be too long), and then calling {toEthSignedMessageHash} on it.
                             */
                            function recover(bytes32 hash, bytes memory signature)
                                internal
                                pure
                                returns (address)
                            {
                                // Check the signature length
                                if (signature.length != 65) {
                                    revert("ECDSA: invalid signature length");
                                }
                                // Divide the signature in r, s and v variables
                                bytes32 r;
                                bytes32 s;
                                uint8 v;
                                // ecrecover takes the signature parameters, and the only way to get them
                                // currently is to use assembly.
                                // solhint-disable-next-line no-inline-assembly
                                assembly {
                                    r := mload(add(signature, 0x20))
                                    s := mload(add(signature, 0x40))
                                    v := byte(0, mload(add(signature, 0x60)))
                                }
                                return recover(hash, v, r, s);
                            }
                            /**
                             * @dev Overload of {ECDSA-recover-bytes32-bytes-} that receives the `v`,
                             * `r` and `s` signature fields separately.
                             */
                            function recover(
                                bytes32 hash,
                                uint8 v,
                                bytes32 r,
                                bytes32 s
                            ) internal pure returns (address) {
                                // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                                // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                                // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
                                // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                                //
                                // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                                // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                                // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                                // these malleable signatures as well.
                                require(
                                    uint256(s) <=
                                        0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
                                    "ECDSA: invalid signature 's' value"
                                );
                                // If the signature is valid (and not malleable), return the signer address
                                // v > 30 is a special case, we need to adjust hash with "\\x19Ethereum Signed Message:\
                        32"
                                // and v = v - 4
                                address signer;
                                if (v > 30) {
                                    require(
                                        v - 4 == 27 || v - 4 == 28,
                                        "ECDSA: invalid signature 'v' value"
                                    );
                                    signer = ecrecover(toEthSignedMessageHash(hash), v - 4, r, s);
                                } else {
                                    require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");
                                    signer = ecrecover(hash, v, r, s);
                                }
                                require(signer != address(0), "ECDSA: invalid signature");
                                return signer;
                            }
                            /**
                             * @dev Returns an Ethereum Signed Message, created from a `hash`. This
                             * replicates the behavior of the
                             * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
                             * JSON-RPC method.
                             *
                             * See {recover}.
                             */
                            function toEthSignedMessageHash(bytes32 hash)
                                internal
                                pure
                                returns (bytes32)
                            {
                                // 32 is the length in bytes of hash,
                                // enforced by the type signature above
                                return
                                    keccak256(
                                        abi.encodePacked("\\x19Ethereum Signed Message:\
                        32", hash)
                                    );
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        pragma abicoder v2;
                        import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
                        import "@rarible/lazy-mint/contracts/erc-721/LibERC721LazyMint.sol";
                        import "@rarible/lazy-mint/contracts/erc-1155/LibERC1155LazyMint.sol";
                        import "@rarible/exchange-interfaces/contracts/IRoyaltiesProvider.sol";
                        import "@rarible/lib-bp/contracts/BpLibrary.sol";
                        import "./interfaces/ITransferManager.sol";
                        abstract contract RaribleTransferManager is OwnableUpgradeable, ITransferManager {
                            using BpLibrary for uint;
                            using SafeMathUpgradeable for uint;
                            ProtocolFeeData public protocolFee;
                            IRoyaltiesProvider public royaltiesRegistry;
                            //deprecated
                            address private defaultFeeReceiver;
                            // deprecated
                            mapping(address => address) private feeReceivers;
                            /// @dev event that's emitted when ProtocolFeeData buyerAmount changes
                            event BuyerFeeAmountChanged(uint oldValue, uint newValue);
                            /// @dev event that's emitted when ProtocolFeeData sellerAmount changes
                            event SellerFeeAmountChanged(uint oldValue, uint newValue);
                            /// @dev event that's emitted when ProtocolFeeData receiver changes
                            event FeeReceiverChanged(address oldValue, address newValue);
                            /// @dev struct to store protocol fee - receiver address, buyer fee amount (in bp), seller fee amount (in bp)
                            struct ProtocolFeeData {
                                address receiver;
                                uint48 buyerAmount;
                                uint48 sellerAmount;
                            }
                            /**
                                @notice initialises RaribleTransferManager state
                                @param newProtocolFee deprecated
                                @param newDefaultFeeReceiver deprecated
                                @param newRoyaltiesProvider royaltiesRegistry contract address
                             */
                            function __RaribleTransferManager_init_unchained(
                                uint newProtocolFee,
                                address newDefaultFeeReceiver,
                                IRoyaltiesProvider newRoyaltiesProvider
                            ) internal initializer {
                                royaltiesRegistry = newRoyaltiesProvider;
                            }
                            function setRoyaltiesRegistry(IRoyaltiesProvider newRoyaltiesRegistry) external onlyOwner {
                                royaltiesRegistry = newRoyaltiesRegistry;
                            }
                            function setPrtocolFeeReceiver(address _receiver) public onlyOwner {
                                emit FeeReceiverChanged(protocolFee.receiver, _receiver);
                                protocolFee.receiver = _receiver;
                            }
                            function setPrtocolFeeBuyerAmount(uint48 _buyerAmount) public onlyOwner {
                                emit BuyerFeeAmountChanged(protocolFee.buyerAmount, _buyerAmount);
                                protocolFee.buyerAmount = _buyerAmount;
                            }
                            function setPrtocolFeeSellerAmount(uint48 _sellerAmount) public onlyOwner {
                                emit SellerFeeAmountChanged(protocolFee.sellerAmount, _sellerAmount);
                                protocolFee.sellerAmount = _sellerAmount;
                            }
                            function setAllProtocolFeeData(address _receiver, uint48 _buyerAmount, uint48 _sellerAmount) public onlyOwner {
                                setPrtocolFeeReceiver(_receiver);
                                setPrtocolFeeBuyerAmount(_buyerAmount);
                                setPrtocolFeeSellerAmount(_sellerAmount);
                            }
                            /**
                                @notice executes transfers for 2 matched orders
                                @param left DealSide from the left order (see LibDeal.sol)
                                @param right DealSide from the right order (see LibDeal.sol)
                                @param feeSide feeSide of the match
                                @return totalLeftValue - total amount for the left order
                                @return totalRightValue - total amout for the right order
                            */
                            function doTransfers(
                                LibDeal.DealSide memory left,
                                LibDeal.DealSide memory right,
                                LibFeeSide.FeeSide feeSide
                            ) override internal returns (uint totalLeftValue, uint totalRightValue) {
                                totalLeftValue = left.asset.value;
                                totalRightValue = right.asset.value;
                                if (feeSide == LibFeeSide.FeeSide.LEFT) {
                                    totalLeftValue = doTransfersWithFees(left, right, protocolFee);
                                    transferPayouts(right.asset.assetType, right.asset.value, right.from, left.payouts, right.proxy);
                                } else if (feeSide == LibFeeSide.FeeSide.RIGHT) {
                                    totalRightValue = doTransfersWithFees(right, left,protocolFee);
                                    transferPayouts(left.asset.assetType, left.asset.value, left.from, right.payouts, left.proxy);
                                } else {
                                    transferPayouts(left.asset.assetType, left.asset.value, left.from, right.payouts, left.proxy);
                                    transferPayouts(right.asset.assetType, right.asset.value, right.from, left.payouts, right.proxy);
                                }
                            }
                            /**
                                @notice executes the fee-side transfers (payment + fees)
                                @param paymentSide DealSide of the fee-side order
                                @param nftSide  DealSide of the nft-side order
                                @param _protocolFee protocol fee data
                                @return totalAmount of fee-side asset
                            */
                            function doTransfersWithFees(
                                LibDeal.DealSide memory paymentSide,
                                LibDeal.DealSide memory nftSide,
                                ProtocolFeeData memory _protocolFee
                            ) internal returns (uint totalAmount) {
                                uint buyerProtocolFee = paymentSide.protocolFeeEnabled ? _protocolFee.buyerAmount : 0;
                                uint sellerProtocolFee = nftSide.protocolFeeEnabled ? _protocolFee.sellerAmount : 0;
                                totalAmount = calculateTotalAmount(paymentSide.asset.value, buyerProtocolFee, paymentSide.originFees);
                                uint rest = transferProtocolFee(totalAmount, paymentSide.asset.value, paymentSide.from, buyerProtocolFee + sellerProtocolFee, _protocolFee.receiver, paymentSide.asset.assetType, paymentSide.proxy);
                                rest = transferRoyalties(paymentSide.asset.assetType, nftSide.asset.assetType, nftSide.payouts, rest, paymentSide.asset.value, paymentSide.from, paymentSide.proxy);
                                if (
                                    paymentSide.originFees.length  == 1 &&
                                    nftSide.originFees.length  == 1 &&
                                    nftSide.originFees[0].account == paymentSide.originFees[0].account
                                ) { 
                                    LibPart.Part[] memory origin = new  LibPart.Part[](1);
                                    origin[0].account = nftSide.originFees[0].account;
                                    origin[0].value = nftSide.originFees[0].value + paymentSide.originFees[0].value;
                                    (rest,) = transferFees(paymentSide.asset.assetType, rest, paymentSide.asset.value, origin, paymentSide.from, paymentSide.proxy);
                                } else {
                                    (rest,) = transferFees(paymentSide.asset.assetType, rest, paymentSide.asset.value, paymentSide.originFees, paymentSide.from, paymentSide.proxy);
                                    (rest,) = transferFees(paymentSide.asset.assetType, rest, paymentSide.asset.value, nftSide.originFees, paymentSide.from, paymentSide.proxy);
                                }
                                transferPayouts(paymentSide.asset.assetType, rest, paymentSide.from, nftSide.payouts, paymentSide.proxy);
                            }
                            /**
                                @notice transfers protocol fee to protocol fee receiver
                            */
                            function transferProtocolFee(
                                uint totalAmount,
                                uint amount,
                                address from,
                                uint protocolFeeTotal,
                                address protocolFeeReceiver,
                                LibAsset.AssetType memory matchCalculate,
                                address proxy
                            ) internal returns (uint) {
                                (uint rest, uint fee) = subFeeInBp(totalAmount, amount, protocolFeeTotal);
                                if (fee > 0) {
                                    transfer(LibAsset.Asset(matchCalculate, fee), from, protocolFeeReceiver, proxy);
                                }
                                return rest;
                            }
                            /**
                                @notice Transfer royalties. If there is only one royalties receiver and one address in payouts and they match,
                                   nothing is transferred in this function
                                @param paymentAssetType Asset Type which represents payment
                                @param nftAssetType Asset Type which represents NFT to pay royalties for
                                @param payouts Payouts to be made
                                @param rest How much of the amount left after previous transfers
                                @param from owner of the Asset to transfer
                                @param proxy Transfer proxy to use
                                @return How much left after transferring royalties
                            */
                            function transferRoyalties(
                                LibAsset.AssetType memory paymentAssetType,
                                LibAsset.AssetType memory nftAssetType,
                                LibPart.Part[] memory payouts,
                                uint rest,
                                uint amount,
                                address from,
                                address proxy
                            ) internal returns (uint) {
                                LibPart.Part[] memory royalties = getRoyaltiesByAssetType(nftAssetType);
                                if (
                                    royalties.length == 1 &&
                                    payouts.length == 1 &&
                                    royalties[0].account == payouts[0].account
                                ) {
                                    require(royalties[0].value <= 5000, "Royalties are too high (>50%)");
                                    return rest;
                                }
                                (uint result, uint totalRoyalties) = transferFees(paymentAssetType, rest, amount, royalties, from, proxy);
                                require(totalRoyalties <= 5000, "Royalties are too high (>50%)");
                                return result;
                            }
                            /**
                                @notice calculates royalties by asset type. If it's a lazy NFT, then royalties are extracted from asset. otherwise using royaltiesRegistry
                                @param nftAssetType NFT Asset Type to calculate royalties for
                                @return calculated royalties (Array of LibPart.Part)
                            */
                            function getRoyaltiesByAssetType(LibAsset.AssetType memory nftAssetType) internal returns (LibPart.Part[] memory) {
                                if (nftAssetType.assetClass == LibAsset.ERC1155_ASSET_CLASS || nftAssetType.assetClass == LibAsset.ERC721_ASSET_CLASS) {
                                    (address token, uint tokenId) = abi.decode(nftAssetType.data, (address, uint));
                                    return royaltiesRegistry.getRoyalties(token, tokenId);
                                } else if (nftAssetType.assetClass == LibERC1155LazyMint.ERC1155_LAZY_ASSET_CLASS) {
                                    (, LibERC1155LazyMint.Mint1155Data memory data) = abi.decode(nftAssetType.data, (address, LibERC1155LazyMint.Mint1155Data));
                                    return data.royalties;
                                } else if (nftAssetType.assetClass == LibERC721LazyMint.ERC721_LAZY_ASSET_CLASS) {
                                    (, LibERC721LazyMint.Mint721Data memory data) = abi.decode(nftAssetType.data, (address, LibERC721LazyMint.Mint721Data));
                                    return data.royalties;
                                }
                                LibPart.Part[] memory empty;
                                return empty;
                            }
                            /**
                                @notice Transfer fees
                                @param assetType Asset Type to transfer
                                @param rest How much of the amount left after previous transfers
                                @param amount Total amount of the Asset. Used as a base to calculate part from (100%)
                                @param fees Array of LibPart.Part which represents fees to pay
                                @param from owner of the Asset to transfer
                                @param proxy Transfer proxy to use
                                @return newRest how much left after transferring fees
                                @return totalFees total number of fees in bp
                            */
                            function transferFees(
                                LibAsset.AssetType memory assetType,
                                uint rest,
                                uint amount,
                                LibPart.Part[] memory fees,
                                address from,
                                address proxy
                            ) internal returns (uint newRest, uint totalFees) {
                                totalFees = 0;
                                newRest = rest;
                                for (uint256 i = 0; i < fees.length; ++i) {
                                    totalFees = totalFees.add(fees[i].value);
                                    uint feeValue;
                                    (newRest, feeValue) = subFeeInBp(newRest, amount, fees[i].value);
                                    if (feeValue > 0) {
                                        transfer(LibAsset.Asset(assetType, feeValue), from, fees[i].account, proxy);
                                    }
                                }
                            }
                            /**
                                @notice transfers main part of the asset (payout)
                                @param assetType Asset Type to transfer
                                @param amount Amount of the asset to transfer
                                @param from Current owner of the asset
                                @param payouts List of payouts - receivers of the Asset
                                @param proxy Transfer Proxy to use
                            */
                            function transferPayouts(
                                LibAsset.AssetType memory assetType,
                                uint amount,
                                address from,
                                LibPart.Part[] memory payouts,
                                address proxy
                            ) internal {
                                require(payouts.length > 0, "transferPayouts: nothing to transfer");
                                uint sumBps = 0;
                                uint rest = amount;
                                for (uint256 i = 0; i < payouts.length - 1; ++i) {
                                    uint currentAmount = amount.bp(payouts[i].value);
                                    sumBps = sumBps.add(payouts[i].value);
                                    if (currentAmount > 0) {
                                        rest = rest.sub(currentAmount);
                                        transfer(LibAsset.Asset(assetType, currentAmount), from, payouts[i].account, proxy);
                                    }
                                }
                                LibPart.Part memory lastPayout = payouts[payouts.length - 1];
                                sumBps = sumBps.add(lastPayout.value);
                                require(sumBps == 10000, "Sum payouts Bps not equal 100%");
                                if (rest > 0) {
                                    transfer(LibAsset.Asset(assetType, rest), from, lastPayout.account, proxy);
                                }
                            }
                            
                            /**
                                @notice calculates total amount of fee-side asset that is going to be used in match
                                @param amount fee-side order value
                                @param buyerProtocolFee buyer protocol fee
                                @param orderOriginFees fee-side order's origin fee (it adds on top of the amount)
                                @return total amount of fee-side asset
                            */
                            function calculateTotalAmount(
                                uint amount,
                                uint buyerProtocolFee,
                                LibPart.Part[] memory orderOriginFees
                            ) internal pure returns (uint) {
                                
                                uint fees = buyerProtocolFee;
                                for (uint256 i = 0; i < orderOriginFees.length; ++i) {
                                    require(orderOriginFees[i].value <= 10000, "origin fee is too big");
                                    fees = fees + orderOriginFees[i].value;
                                }
                                return amount.add(amount.bp(fees));
                            }
                            function subFeeInBp(uint value, uint total, uint feeInBp) internal pure returns (uint newValue, uint realFee) {
                                return subFee(value, total.bp(feeInBp));
                            }
                            function subFee(uint value, uint fee) internal pure returns (uint newValue, uint realFee) {
                                if (value > fee) {
                                    newValue = value.sub(fee);
                                    realFee = fee;
                                } else {
                                    newValue = 0;
                                    realFee = value;
                                }
                            }
                            uint256[46] private __gap;
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        pragma abicoder v2;
                        import "@rarible/exchange-interfaces/contracts/ITransferProxy.sol";
                        import "@rarible/exchange-interfaces/contracts/INftTransferProxy.sol";
                        import "@rarible/exchange-interfaces/contracts/IERC20TransferProxy.sol";
                        import "./interfaces/ITransferExecutor.sol";
                        import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol";
                        import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
                        import "./lib/LibTransfer.sol";
                        abstract contract TransferExecutor is Initializable, OwnableUpgradeable, ITransferExecutor {
                            using LibTransfer for address;
                            mapping (bytes4 => address) internal proxies;
                            event ProxyChange(bytes4 indexed assetType, address proxy);
                            function __TransferExecutor_init_unchained(address transferProxy, address erc20TransferProxy) internal { 
                                proxies[LibAsset.ERC20_ASSET_CLASS] = address(erc20TransferProxy);
                                proxies[LibAsset.ERC721_ASSET_CLASS] = address(transferProxy);
                                proxies[LibAsset.ERC1155_ASSET_CLASS] = address(transferProxy);
                            }
                            function setTransferProxy(bytes4 assetType, address proxy) external onlyOwner {
                                proxies[assetType] = proxy;
                                emit ProxyChange(assetType, proxy);
                            }
                            function transfer(
                                LibAsset.Asset memory asset,
                                address from,
                                address to,
                                address proxy
                            ) internal override {
                                if (asset.assetType.assetClass == LibAsset.ERC721_ASSET_CLASS) {
                                    //not using transfer proxy when transfering from this contract
                                    (address token, uint tokenId) = abi.decode(asset.assetType.data, (address, uint256));
                                    require(asset.value == 1, "erc721 value error");
                                    if (from == address(this)){
                                        IERC721Upgradeable(token).safeTransferFrom(address(this), to, tokenId);
                                    } else {
                                        INftTransferProxy(proxy).erc721safeTransferFrom(IERC721Upgradeable(token), from, to, tokenId);
                                    }
                                } else if (asset.assetType.assetClass == LibAsset.ERC20_ASSET_CLASS) {
                                    //not using transfer proxy when transfering from this contract
                                    (address token) = abi.decode(asset.assetType.data, (address));
                                    if (from == address(this)){
                                        require(IERC20Upgradeable(token).transfer(to, asset.value), "erc20 transfer failed");
                                    } else {
                                        IERC20TransferProxy(proxy).erc20safeTransferFrom(IERC20Upgradeable(token), from, to, asset.value);
                                    }
                                } else if (asset.assetType.assetClass == LibAsset.ERC1155_ASSET_CLASS) {
                                    //not using transfer proxy when transfering from this contract
                                    (address token, uint tokenId) = abi.decode(asset.assetType.data, (address, uint256));
                                    if (from == address(this)){
                                        IERC1155Upgradeable(token).safeTransferFrom(address(this), to, tokenId, asset.value, "");
                                    } else {
                                        INftTransferProxy(proxy).erc1155safeTransferFrom(IERC1155Upgradeable(token), from, to, tokenId, asset.value, "");  
                                    }
                                } else if (asset.assetType.assetClass == LibAsset.ETH_ASSET_CLASS) {
                                    if (to != address(this)) {
                                        to.transferEth(asset.value);
                                    }
                                } else {
                                    ITransferProxy(proxy).transfer(asset, from, to);
                                }
                            }
                            
                            uint256[49] private __gap;
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        pragma abicoder v2;
                        import "@rarible/lib-asset/contracts/LibAsset.sol";
                        abstract contract ITransferExecutor {
                            function transfer(
                                LibAsset.Asset memory asset,
                                address from,
                                address to,
                                address proxy
                            ) internal virtual;
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        pragma abicoder v2;
                        import "../lib/LibDeal.sol";
                        import "./ITransferExecutor.sol";
                        abstract contract ITransferManager is ITransferExecutor {
                            function doTransfers(
                                LibDeal.DealSide memory left,
                                LibDeal.DealSide memory right,
                                LibFeeSide.FeeSide feeSide
                            ) internal virtual returns (uint totalMakeValue, uint totalTakeValue);
                        }// SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        pragma abicoder v2;
                        import "@rarible/lib-part/contracts/LibPart.sol";
                        import "@rarible/lib-asset/contracts/LibAsset.sol";
                        import "./LibFeeSide.sol";
                        library LibDeal {
                            struct DealSide {
                                LibAsset.Asset asset;
                                LibPart.Part[] payouts;
                                LibPart.Part[] originFees;
                                address proxy;
                                address from;
                                bool protocolFeeEnabled;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        import "@rarible/lib-asset/contracts/LibAsset.sol";
                        library LibFeeSide {
                            enum FeeSide {NONE, LEFT, RIGHT}
                            function getFeeSide(bytes4 leftClass, bytes4 rightClass) internal pure returns (FeeSide) {
                                if (leftClass == LibAsset.ETH_ASSET_CLASS) {
                                    return FeeSide.LEFT;
                                }
                                if (rightClass == LibAsset.ETH_ASSET_CLASS) {
                                    return FeeSide.RIGHT;
                                }
                                if (leftClass == LibAsset.ERC20_ASSET_CLASS) {
                                    return FeeSide.LEFT;
                                }
                                if (rightClass == LibAsset.ERC20_ASSET_CLASS) {
                                    return FeeSide.RIGHT;
                                }
                                if (leftClass == LibAsset.ERC1155_ASSET_CLASS) {
                                    return FeeSide.LEFT;
                                }
                                if (rightClass == LibAsset.ERC1155_ASSET_CLASS) {
                                    return FeeSide.RIGHT;
                                }
                                return FeeSide.NONE;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.7.6;
                        library LibTransfer {
                            function transferEth(address to, uint value) internal {
                                (bool success,) = to.call{ value: value }("");
                                require(success, "transfer failed");
                            }
                        }
                        

                        File 7 of 11: ERC20TransferProxy
                        pragma solidity ^0.5.0;
                        
                        
                        /*
                         * @dev Provides information about the current execution context, including the
                         * sender of the transaction and its data. While these are generally available
                         * via msg.sender and msg.data, they should not be accessed in such a direct
                         * manner, since when dealing with GSN meta-transactions the account sending and
                         * paying for execution may not be the actual sender (as far as an application
                         * is concerned).
                         *
                         * This contract is only required for intermediate, library-like contracts.
                         */
                        contract Context {
                            // Empty internal constructor, to prevent people from mistakenly deploying
                            // an instance of this contract, which should be used via inheritance.
                            constructor () internal { }
                            // solhint-disable-previous-line no-empty-blocks
                        
                            function _msgSender() internal view returns (address payable) {
                                return msg.sender;
                            }
                        
                            function _msgData() internal view returns (bytes memory) {
                                this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                                return msg.data;
                            }
                        }
                        
                        /**
                         * @title Roles
                         * @dev Library for managing addresses assigned to a Role.
                         */
                        library Roles {
                            struct Role {
                                mapping (address => bool) bearer;
                            }
                        
                            /**
                             * @dev Give an account access to this role.
                             */
                            function add(Role storage role, address account) internal {
                                require(!has(role, account), "Roles: account already has role");
                                role.bearer[account] = true;
                            }
                        
                            /**
                             * @dev Remove an account's access to this role.
                             */
                            function remove(Role storage role, address account) internal {
                                require(has(role, account), "Roles: account does not have role");
                                role.bearer[account] = false;
                            }
                        
                            /**
                             * @dev Check if an account has this role.
                             * @return bool
                             */
                            function has(Role storage role, address account) internal view returns (bool) {
                                require(account != address(0), "Roles: account is the zero address");
                                return role.bearer[account];
                            }
                        }
                        
                        contract OperatorRole is Context {
                            using Roles for Roles.Role;
                        
                            event OperatorAdded(address indexed account);
                            event OperatorRemoved(address indexed account);
                        
                            Roles.Role private _operators;
                        
                            constructor () internal {
                        
                            }
                        
                            modifier onlyOperator() {
                                require(isOperator(_msgSender()), "OperatorRole: caller does not have the Operator role");
                                _;
                            }
                        
                            function isOperator(address account) public view returns (bool) {
                                return _operators.has(account);
                            }
                        
                            function _addOperator(address account) internal {
                                _operators.add(account);
                                emit OperatorAdded(account);
                            }
                        
                            function _removeOperator(address account) internal {
                                _operators.remove(account);
                                emit OperatorRemoved(account);
                            }
                        }
                        
                        /**
                         * @dev Contract module which provides a basic access control mechanism, where
                         * there is an account (an owner) that can be granted exclusive access to
                         * specific functions.
                         *
                         * This module is used through inheritance. It will make available the modifier
                         * `onlyOwner`, which can be applied to your functions to restrict their use to
                         * the owner.
                         */
                        contract Ownable is Context {
                            address private _owner;
                        
                            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                        
                            /**
                             * @dev Initializes the contract setting the deployer as the initial owner.
                             */
                            constructor () internal {
                                address msgSender = _msgSender();
                                _owner = msgSender;
                                emit OwnershipTransferred(address(0), msgSender);
                            }
                        
                            /**
                             * @dev Returns the address of the current owner.
                             */
                            function owner() public view returns (address) {
                                return _owner;
                            }
                        
                            /**
                             * @dev Throws if called by any account other than the owner.
                             */
                            modifier onlyOwner() {
                                require(isOwner(), "Ownable: caller is not the owner");
                                _;
                            }
                        
                            /**
                             * @dev Returns true if the caller is the current owner.
                             */
                            function isOwner() public view returns (bool) {
                                return _msgSender() == _owner;
                            }
                        
                            /**
                             * @dev Leaves the contract without owner. It will not be possible to call
                             * `onlyOwner` functions anymore. Can only be called by the current owner.
                             *
                             * NOTE: Renouncing ownership will leave the contract without an owner,
                             * thereby removing any functionality that is only available to the owner.
                             */
                            function renounceOwnership() public onlyOwner {
                                emit OwnershipTransferred(_owner, address(0));
                                _owner = address(0);
                            }
                        
                            /**
                             * @dev Transfers ownership of the contract to a new account (`newOwner`).
                             * Can only be called by the current owner.
                             */
                            function transferOwnership(address newOwner) public onlyOwner {
                                _transferOwnership(newOwner);
                            }
                        
                            /**
                             * @dev Transfers ownership of the contract to a new account (`newOwner`).
                             */
                            function _transferOwnership(address newOwner) internal {
                                require(newOwner != address(0), "Ownable: new owner is the zero address");
                                emit OwnershipTransferred(_owner, newOwner);
                                _owner = newOwner;
                            }
                        }
                        
                        contract OwnableOperatorRole is Ownable, OperatorRole {
                            function addOperator(address account) external onlyOwner {
                                _addOperator(account);
                            }
                        
                            function removeOperator(address account) external onlyOwner {
                                _removeOperator(account);
                            }
                        }
                        
                        /**
                         * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
                         * the optional functions; to access them see {ERC20Detailed}.
                         */
                        interface IERC20 {
                            /**
                             * @dev Returns the amount of tokens in existence.
                             */
                            function totalSupply() external view returns (uint256);
                        
                            /**
                             * @dev Returns the amount of tokens owned by `account`.
                             */
                            function balanceOf(address account) external view returns (uint256);
                        
                            /**
                             * @dev Moves `amount` tokens from the caller's account to `recipient`.
                             *
                             * Returns a boolean value indicating whether the operation succeeded.
                             *
                             * Emits a {Transfer} event.
                             */
                            function transfer(address recipient, uint256 amount) external returns (bool);
                        
                            /**
                             * @dev Returns the remaining number of tokens that `spender` will be
                             * allowed to spend on behalf of `owner` through {transferFrom}. This is
                             * zero by default.
                             *
                             * This value changes when {approve} or {transferFrom} are called.
                             */
                            function allowance(address owner, address spender) external view returns (uint256);
                        
                            /**
                             * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                             *
                             * Returns a boolean value indicating whether the operation succeeded.
                             *
                             * IMPORTANT: Beware that changing an allowance with this method brings the risk
                             * that someone may use both the old and the new allowance by unfortunate
                             * transaction ordering. One possible solution to mitigate this race
                             * condition is to first reduce the spender's allowance to 0 and set the
                             * desired value afterwards:
                             * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                             *
                             * Emits an {Approval} event.
                             */
                            function approve(address spender, uint256 amount) external returns (bool);
                        
                            /**
                             * @dev Moves `amount` tokens from `sender` to `recipient` using the
                             * allowance mechanism. `amount` is then deducted from the caller's
                             * allowance.
                             *
                             * Returns a boolean value indicating whether the operation succeeded.
                             *
                             * Emits a {Transfer} event.
                             */
                            function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
                        
                            /**
                             * @dev Emitted when `value` tokens are moved from one account (`from`) to
                             * another (`to`).
                             *
                             * Note that `value` may be zero.
                             */
                            event Transfer(address indexed from, address indexed to, uint256 value);
                        
                            /**
                             * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                             * a call to {approve}. `value` is the new allowance.
                             */
                            event Approval(address indexed owner, address indexed spender, uint256 value);
                        }
                        
                        contract ERC20TransferProxy is OwnableOperatorRole {
                        
                            function erc20safeTransferFrom(IERC20 token, address from, address to, uint256 value) external onlyOperator {
                                require(token.transferFrom(from, to, value), "failure while transferring");
                            }
                        }

                        File 8 of 11: TransparentUpgradeableProxy
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
                        import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
                        import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
                        // Kept for backwards compatibility with older versions of Hardhat and Truffle plugins.
                        contract AdminUpgradeabilityProxy is TransparentUpgradeableProxy {
                            constructor(address logic, address admin, bytes memory data) payable TransparentUpgradeableProxy(logic, admin, data) {}
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        import "../Proxy.sol";
                        import "./ERC1967Upgrade.sol";
                        /**
                         * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
                         * implementation address that can be changed. This address is stored in storage in the location specified by
                         * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
                         * implementation behind the proxy.
                         */
                        contract ERC1967Proxy is Proxy, ERC1967Upgrade {
                            /**
                             * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
                             *
                             * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
                             * function call, and allows initializating the storage of the proxy like a Solidity constructor.
                             */
                            constructor(address _logic, bytes memory _data) payable {
                                assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                                _upgradeToAndCall(_logic, _data, false);
                            }
                            /**
                             * @dev Returns the current implementation address.
                             */
                            function _implementation() internal view virtual override returns (address impl) {
                                return ERC1967Upgrade._getImplementation();
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        import "../ERC1967/ERC1967Proxy.sol";
                        /**
                         * @dev This contract implements a proxy that is upgradeable by an admin.
                         *
                         * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
                         * clashing], which can potentially be used in an attack, this contract uses the
                         * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
                         * things that go hand in hand:
                         *
                         * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
                         * that call matches one of the admin functions exposed by the proxy itself.
                         * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
                         * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
                         * "admin cannot fallback to proxy target".
                         *
                         * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
                         * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
                         * to sudden errors when trying to call a function from the proxy implementation.
                         *
                         * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
                         * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
                         */
                        contract TransparentUpgradeableProxy is ERC1967Proxy {
                            /**
                             * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                             * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
                             */
                            constructor(address _logic, address admin_, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
                                assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                                _changeAdmin(admin_);
                            }
                            /**
                             * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                             */
                            modifier ifAdmin() {
                                if (msg.sender == _getAdmin()) {
                                    _;
                                } else {
                                    _fallback();
                                }
                            }
                            /**
                             * @dev Returns the current admin.
                             *
                             * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
                             *
                             * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                             * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                             * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                             */
                            function admin() external ifAdmin returns (address admin_) {
                                admin_ = _getAdmin();
                            }
                            /**
                             * @dev Returns the current implementation.
                             *
                             * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
                             *
                             * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                             * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                             * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                             */
                            function implementation() external ifAdmin returns (address implementation_) {
                                implementation_ = _implementation();
                            }
                            /**
                             * @dev Changes the admin of the proxy.
                             *
                             * Emits an {AdminChanged} event.
                             *
                             * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
                             */
                            function changeAdmin(address newAdmin) external virtual ifAdmin {
                                _changeAdmin(newAdmin);
                            }
                            /**
                             * @dev Upgrade the implementation of the proxy.
                             *
                             * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
                             */
                            function upgradeTo(address newImplementation) external ifAdmin {
                                _upgradeToAndCall(newImplementation, bytes(""), false);
                            }
                            /**
                             * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                             * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                             * proxied contract.
                             *
                             * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
                             */
                            function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
                                _upgradeToAndCall(newImplementation, data, true);
                            }
                            /**
                             * @dev Returns the current admin.
                             */
                            function _admin() internal view virtual returns (address) {
                                return _getAdmin();
                            }
                            /**
                             * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
                             */
                            function _beforeFallback() internal virtual override {
                                require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                                super._beforeFallback();
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        import "./TransparentUpgradeableProxy.sol";
                        import "../../access/Ownable.sol";
                        /**
                         * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
                         * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
                         */
                        contract ProxyAdmin is Ownable {
                            /**
                             * @dev Returns the current implementation of `proxy`.
                             *
                             * Requirements:
                             *
                             * - This contract must be the admin of `proxy`.
                             */
                            function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
                                // We need to manually run the static call since the getter cannot be flagged as view
                                // bytes4(keccak256("implementation()")) == 0x5c60da1b
                                (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
                                require(success);
                                return abi.decode(returndata, (address));
                            }
                            /**
                             * @dev Returns the current admin of `proxy`.
                             *
                             * Requirements:
                             *
                             * - This contract must be the admin of `proxy`.
                             */
                            function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
                                // We need to manually run the static call since the getter cannot be flagged as view
                                // bytes4(keccak256("admin()")) == 0xf851a440
                                (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
                                require(success);
                                return abi.decode(returndata, (address));
                            }
                            /**
                             * @dev Changes the admin of `proxy` to `newAdmin`.
                             *
                             * Requirements:
                             *
                             * - This contract must be the current admin of `proxy`.
                             */
                            function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {
                                proxy.changeAdmin(newAdmin);
                            }
                            /**
                             * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
                             *
                             * Requirements:
                             *
                             * - This contract must be the admin of `proxy`.
                             */
                            function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {
                                proxy.upgradeTo(implementation);
                            }
                            /**
                             * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
                             * {TransparentUpgradeableProxy-upgradeToAndCall}.
                             *
                             * Requirements:
                             *
                             * - This contract must be the admin of `proxy`.
                             */
                            function upgradeAndCall(TransparentUpgradeableProxy proxy, address implementation, bytes memory data) public payable virtual onlyOwner {
                                proxy.upgradeToAndCall{value: msg.value}(implementation, data);
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        /**
                         * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
                         * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
                         * be specified by overriding the virtual {_implementation} function.
                         *
                         * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
                         * different contract through the {_delegate} function.
                         *
                         * The success and return data of the delegated call will be returned back to the caller of the proxy.
                         */
                        abstract contract Proxy {
                            /**
                             * @dev Delegates the current call to `implementation`.
                             *
                             * This function does not return to its internall call site, it will return directly to the external caller.
                             */
                            function _delegate(address implementation) internal virtual {
                                // solhint-disable-next-line no-inline-assembly
                                assembly {
                                    // Copy msg.data. We take full control of memory in this inline assembly
                                    // block because it will not return to Solidity code. We overwrite the
                                    // Solidity scratch pad at memory position 0.
                                    calldatacopy(0, 0, calldatasize())
                                    // Call the implementation.
                                    // out and outsize are 0 because we don't know the size yet.
                                    let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                                    // Copy the returned data.
                                    returndatacopy(0, 0, returndatasize())
                                    switch result
                                    // delegatecall returns 0 on error.
                                    case 0 { revert(0, returndatasize()) }
                                    default { return(0, returndatasize()) }
                                }
                            }
                            /**
                             * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
                             * and {_fallback} should delegate.
                             */
                            function _implementation() internal view virtual returns (address);
                            /**
                             * @dev Delegates the current call to the address returned by `_implementation()`.
                             *
                             * This function does not return to its internall call site, it will return directly to the external caller.
                             */
                            function _fallback() internal virtual {
                                _beforeFallback();
                                _delegate(_implementation());
                            }
                            /**
                             * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
                             * function in the contract matches the call data.
                             */
                            fallback () external payable virtual {
                                _fallback();
                            }
                            /**
                             * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                             * is empty.
                             */
                            receive () external payable virtual {
                                _fallback();
                            }
                            /**
                             * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
                             * call, or as part of the Solidity `fallback` or `receive` functions.
                             *
                             * If overriden should call `super._beforeFallback()`.
                             */
                            function _beforeFallback() internal virtual {
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.2;
                        import "../beacon/IBeacon.sol";
                        import "../../utils/Address.sol";
                        import "../../utils/StorageSlot.sol";
                        /**
                         * @dev This abstract contract provides getters and event emitting update functions for
                         * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
                         *
                         * _Available since v4.1._
                         *
                         * @custom:oz-upgrades-unsafe-allow delegatecall
                         */
                        abstract contract ERC1967Upgrade {
                            // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                            bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                            /**
                             * @dev Storage slot with the address of the current implementation.
                             * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                             * validated in the constructor.
                             */
                            bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                            /**
                             * @dev Emitted when the implementation is upgraded.
                             */
                            event Upgraded(address indexed implementation);
                            /**
                             * @dev Returns the current implementation address.
                             */
                            function _getImplementation() internal view returns (address) {
                                return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                            }
                            /**
                             * @dev Stores a new address in the EIP1967 implementation slot.
                             */
                            function _setImplementation(address newImplementation) private {
                                require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                                StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                            }
                            /**
                             * @dev Perform implementation upgrade
                             *
                             * Emits an {Upgraded} event.
                             */
                            function _upgradeTo(address newImplementation) internal {
                                _setImplementation(newImplementation);
                                emit Upgraded(newImplementation);
                            }
                            /**
                             * @dev Perform implementation upgrade with additional setup call.
                             *
                             * Emits an {Upgraded} event.
                             */
                            function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
                                _setImplementation(newImplementation);
                                emit Upgraded(newImplementation);
                                if (data.length > 0 || forceCall) {
                                    Address.functionDelegateCall(newImplementation, data);
                                }
                            }
                            /**
                             * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                             *
                             * Emits an {Upgraded} event.
                             */
                            function _upgradeToAndCallSecure(address newImplementation, bytes memory data, bool forceCall) internal {
                                address oldImplementation = _getImplementation();
                                // Initial upgrade and setup call
                                _setImplementation(newImplementation);
                                if (data.length > 0 || forceCall) {
                                    Address.functionDelegateCall(newImplementation, data);
                                }
                                // Perform rollback test if not already in progress
                                StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);
                                if (!rollbackTesting.value) {
                                    // Trigger rollback using upgradeTo from the new implementation
                                    rollbackTesting.value = true;
                                    Address.functionDelegateCall(
                                        newImplementation,
                                        abi.encodeWithSignature(
                                            "upgradeTo(address)",
                                            oldImplementation
                                        )
                                    );
                                    rollbackTesting.value = false;
                                    // Check rollback was effective
                                    require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
                                    // Finally reset to the new implementation and log the upgrade
                                    _setImplementation(newImplementation);
                                    emit Upgraded(newImplementation);
                                }
                            }
                            /**
                             * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                             * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                             *
                             * Emits a {BeaconUpgraded} event.
                             */
                            function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
                                _setBeacon(newBeacon);
                                emit BeaconUpgraded(newBeacon);
                                if (data.length > 0 || forceCall) {
                                    Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                                }
                            }
                            /**
                             * @dev Storage slot with the admin of the contract.
                             * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                             * validated in the constructor.
                             */
                            bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                            /**
                             * @dev Emitted when the admin account has changed.
                             */
                            event AdminChanged(address previousAdmin, address newAdmin);
                            /**
                             * @dev Returns the current admin.
                             */
                            function _getAdmin() internal view returns (address) {
                                return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
                            }
                            /**
                             * @dev Stores a new address in the EIP1967 admin slot.
                             */
                            function _setAdmin(address newAdmin) private {
                                require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                                StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                            }
                            /**
                             * @dev Changes the admin of the proxy.
                             *
                             * Emits an {AdminChanged} event.
                             */
                            function _changeAdmin(address newAdmin) internal {
                                emit AdminChanged(_getAdmin(), newAdmin);
                                _setAdmin(newAdmin);
                            }
                            /**
                             * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                             * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                             */
                            bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                            /**
                             * @dev Emitted when the beacon is upgraded.
                             */
                            event BeaconUpgraded(address indexed beacon);
                            /**
                             * @dev Returns the current beacon.
                             */
                            function _getBeacon() internal view returns (address) {
                                return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
                            }
                            /**
                             * @dev Stores a new beacon in the EIP1967 beacon slot.
                             */
                            function _setBeacon(address newBeacon) private {
                                require(
                                    Address.isContract(newBeacon),
                                    "ERC1967: new beacon is not a contract"
                                );
                                require(
                                    Address.isContract(IBeacon(newBeacon).implementation()),
                                    "ERC1967: beacon implementation is not a contract"
                                );
                                StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        /**
                         * @dev This is the interface that {BeaconProxy} expects of its beacon.
                         */
                        interface IBeacon {
                            /**
                             * @dev Must return an address that can be used as a delegate call target.
                             *
                             * {BeaconProxy} will check that this address is a contract.
                             */
                            function implementation() external view returns (address);
                        }
                        // SPDX-License-Identifier: MIT
                        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;
                                // solhint-disable-next-line no-inline-assembly
                                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");
                                // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                                (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");
                                // solhint-disable-next-line avoid-low-level-calls
                                (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");
                                // solhint-disable-next-line avoid-low-level-calls
                                (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");
                                // solhint-disable-next-line avoid-low-level-calls
                                (bool success, bytes memory returndata) = target.delegatecall(data);
                                return _verifyCallResult(success, returndata, errorMessage);
                            }
                            function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private 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
                                        // solhint-disable-next-line no-inline-assembly
                                        assembly {
                                            let returndata_size := mload(returndata)
                                            revert(add(32, returndata), returndata_size)
                                        }
                                    } else {
                                        revert(errorMessage);
                                    }
                                }
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        /**
                         * @dev Library for reading and writing primitive types to specific storage slots.
                         *
                         * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
                         * This library helps with reading and writing to such slots without the need for inline assembly.
                         *
                         * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
                         *
                         * Example usage to set ERC1967 implementation slot:
                         * ```
                         * contract ERC1967 {
                         *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                         *
                         *     function _getImplementation() internal view returns (address) {
                         *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                         *     }
                         *
                         *     function _setImplementation(address newImplementation) internal {
                         *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                         *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                         *     }
                         * }
                         * ```
                         *
                         * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
                         */
                        library StorageSlot {
                            struct AddressSlot {
                                address value;
                            }
                            struct BooleanSlot {
                                bool value;
                            }
                            struct Bytes32Slot {
                                bytes32 value;
                            }
                            struct Uint256Slot {
                                uint256 value;
                            }
                            /**
                             * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                             */
                            function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                                assembly {
                                    r.slot := slot
                                }
                            }
                            /**
                             * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                             */
                            function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                                assembly {
                                    r.slot := slot
                                }
                            }
                            /**
                             * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                             */
                            function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                                assembly {
                                    r.slot := slot
                                }
                            }
                            /**
                             * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                             */
                            function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                                assembly {
                                    r.slot := slot
                                }
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        import "../utils/Context.sol";
                        /**
                         * @dev Contract module which provides a basic access control mechanism, where
                         * there is an account (an owner) that can be granted exclusive access to
                         * specific functions.
                         *
                         * By default, the owner account will be the one that deploys the contract. This
                         * can later be changed with {transferOwnership}.
                         *
                         * This module is used through inheritance. It will make available the modifier
                         * `onlyOwner`, which can be applied to your functions to restrict their use to
                         * the owner.
                         */
                        abstract contract Ownable is Context {
                            address private _owner;
                            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                            /**
                             * @dev Initializes the contract setting the deployer as the initial owner.
                             */
                            constructor () {
                                address msgSender = _msgSender();
                                _owner = msgSender;
                                emit OwnershipTransferred(address(0), msgSender);
                            }
                            /**
                             * @dev Returns the address of the current owner.
                             */
                            function owner() public view virtual returns (address) {
                                return _owner;
                            }
                            /**
                             * @dev Throws if called by any account other than the owner.
                             */
                            modifier onlyOwner() {
                                require(owner() == _msgSender(), "Ownable: caller is not the owner");
                                _;
                            }
                            /**
                             * @dev Leaves the contract without owner. It will not be possible to call
                             * `onlyOwner` functions anymore. Can only be called by the current owner.
                             *
                             * NOTE: Renouncing ownership will leave the contract without an owner,
                             * thereby removing any functionality that is only available to the owner.
                             */
                            function renounceOwnership() public virtual onlyOwner {
                                emit OwnershipTransferred(_owner, address(0));
                                _owner = address(0);
                            }
                            /**
                             * @dev Transfers ownership of the contract to a new account (`newOwner`).
                             * Can only be called by the current owner.
                             */
                            function transferOwnership(address newOwner) public virtual onlyOwner {
                                require(newOwner != address(0), "Ownable: new owner is the zero address");
                                emit OwnershipTransferred(_owner, newOwner);
                                _owner = newOwner;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        /*
                         * @dev Provides information about the current execution context, including the
                         * sender of the transaction and its data. While these are generally available
                         * via msg.sender and msg.data, they should not be accessed in such a direct
                         * manner, since when dealing with meta-transactions the account sending and
                         * paying for execution may not be the actual sender (as far as an application
                         * is concerned).
                         *
                         * This contract is only required for intermediate, library-like contracts.
                         */
                        abstract contract Context {
                            function _msgSender() internal view virtual returns (address) {
                                return msg.sender;
                            }
                            function _msgData() internal view virtual returns (bytes calldata) {
                                this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                                return msg.data;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        import "../ERC1967/ERC1967Upgrade.sol";
                        /**
                         * @dev Base contract for building openzeppelin-upgrades compatible implementations for the {ERC1967Proxy}. It includes
                         * publicly available upgrade functions that are called by the plugin and by the secure upgrade mechanism to verify
                         * continuation of the upgradability.
                         *
                         * The {_authorizeUpgrade} function MUST be overridden to include access restriction to the upgrade mechanism.
                         *
                         * _Available since v4.1._
                         */
                        abstract contract UUPSUpgradeable is ERC1967Upgrade {
                            function upgradeTo(address newImplementation) external virtual {
                                _authorizeUpgrade(newImplementation);
                                _upgradeToAndCallSecure(newImplementation, bytes(""), false);
                            }
                            function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual {
                                _authorizeUpgrade(newImplementation);
                                _upgradeToAndCallSecure(newImplementation, data, true);
                            }
                            function _authorizeUpgrade(address newImplementation) internal virtual;
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.2;
                        import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
                        abstract contract Proxiable is UUPSUpgradeable {
                            function _authorizeUpgrade(address newImplementation) internal override {
                                _beforeUpgrade(newImplementation);
                            }
                            function _beforeUpgrade(address newImplementation) internal virtual;
                        }
                        contract ChildOfProxiable is Proxiable {
                            function _beforeUpgrade(address newImplementation) internal virtual override {}
                        }
                        

                        File 9 of 11: RoyaltiesRegistry
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.2 <0.8.0;
                        pragma abicoder v2;
                        import "@rarible/exchange-interfaces/contracts/IRoyaltiesProvider.sol";
                        import "@rarible/royalties/contracts/LibRoyaltiesV2.sol";
                        import "@rarible/royalties/contracts/LibRoyaltiesV1.sol";
                        import "@rarible/royalties/contracts/LibRoyalties2981.sol";
                        import "@rarible/royalties/contracts/RoyaltiesV1.sol";
                        import "@rarible/royalties/contracts/RoyaltiesV2.sol";
                        import "@rarible/royalties/contracts/IERC2981.sol";
                        import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
                        import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
                        contract RoyaltiesRegistry is IRoyaltiesProvider, OwnableUpgradeable {
                            /// @dev deprecated
                            event RoyaltiesSetForToken(address indexed token, uint indexed tokenId, LibPart.Part[] royalties);
                            /// @dev emitted when royalties set for token in 
                            event RoyaltiesSetForContract(address indexed token, LibPart.Part[] royalties);
                            /// @dev struct to store royalties in royaltiesByToken
                            struct RoyaltiesSet {
                                bool initialized;
                                LibPart.Part[] royalties;
                            }
                            /// @dev deprecated
                            mapping(bytes32 => RoyaltiesSet) public royaltiesByTokenAndTokenId;
                            /// @dev stores royalties for token contract, set in setRoyaltiesByToken() method
                            mapping(address => RoyaltiesSet) public royaltiesByToken;
                            /// @dev stores external provider and royalties type for token contract
                            mapping(address => uint) public royaltiesProviders;
                            /// @dev total amount or supported royalties types
                            // 0 - royalties type is unset
                            // 1 - royaltiesByToken, 2 - v2, 3 - v1,
                            // 4 - external provider, 5 - EIP-2981
                            // 6 - unsupported/nonexistent royalties type
                            uint constant royaltiesTypesAmount = 6;
                            function __RoyaltiesRegistry_init() external initializer {
                                __Ownable_init_unchained();
                            }
                            /// @dev sets external provider for token contract, and royalties type = 4
                            function setProviderByToken(address token, address provider) external {
                                checkOwner(token);
                                setRoyaltiesType(token, 4, provider);
                            }
                            /// @dev returns provider address for token contract from royaltiesProviders mapping
                            function getProvider(address token) public view returns(address) {
                                return address(royaltiesProviders[token]);
                            }
                            /// @dev returns royalties type for token contract
                            function getRoyaltiesType(address token) external view returns(uint) {
                                return _getRoyaltiesType(royaltiesProviders[token]);
                            }
                            /// @dev returns royalties type from uint
                            function _getRoyaltiesType(uint data) internal pure returns(uint) {
                                for (uint i = 1; i <= royaltiesTypesAmount; i++) {
                                    if (data / 2**(256-i) == 1) {
                                        return i;
                                    }
                                }
                                return 0;
                            }
                            /// @dev sets royalties type for token contract
                            function setRoyaltiesType(address token, uint royaltiesType, address royaltiesProvider) internal {
                                require(royaltiesType > 0 && royaltiesType <= royaltiesTypesAmount, "wrong royaltiesType");
                                royaltiesProviders[token] = uint(royaltiesProvider) + 2**(256 - royaltiesType);
                            }
                            /// @dev clears and sets new royalties type for token contract
                            function forceSetRoyaltiesType(address token, uint royaltiesType) external {
                                checkOwner(token);
                                setRoyaltiesType(token, royaltiesType, getProvider(token));
                            }
                            /// @dev clears royalties type for token contract
                            function clearRoyaltiesType(address token) external {
                                checkOwner(token);
                                royaltiesProviders[token] = uint(getProvider(token));
                            }
                            /// @dev sets royalties for token contract in royaltiesByToken mapping and royalties type = 1
                            function setRoyaltiesByToken(address token, LibPart.Part[] memory royalties) external {
                                checkOwner(token);
                                //clearing royaltiesProviders value for the token
                                delete royaltiesProviders[token];
                                // setting royaltiesType = 1 for the token
                                setRoyaltiesType(token, 1, address(0));
                                uint sumRoyalties = 0;
                                delete royaltiesByToken[token];
                                for (uint i = 0; i < royalties.length; i++) {
                                    require(royalties[i].account != address(0x0), "RoyaltiesByToken recipient should be present");
                                    require(royalties[i].value != 0, "Royalty value for RoyaltiesByToken should be > 0");
                                    royaltiesByToken[token].royalties.push(royalties[i]);
                                    sumRoyalties += royalties[i].value;
                                }
                                require(sumRoyalties < 10000, "Set by token royalties sum more, than 100%");
                                royaltiesByToken[token].initialized = true;
                                emit RoyaltiesSetForContract(token, royalties);
                            }
                            /// @dev checks if msg.sender is owner of this contract or owner of the token contract
                            function checkOwner(address token) internal view {
                                if ((owner() != _msgSender()) && (OwnableUpgradeable(token).owner() != _msgSender())) {
                                    revert("Token owner not detected");
                                }
                            }
                            /// @dev calculates royalties type for token contract
                            function calculateRoyaltiesType(address token, address royaltiesProvider ) internal view returns(uint) {   
                                try IERC165Upgradeable(token).supportsInterface(LibRoyaltiesV2._INTERFACE_ID_ROYALTIES) returns(bool result) {
                                    if (result) {
                                        return 2;
                                    }
                                } catch { }
                                try IERC165Upgradeable(token).supportsInterface(LibRoyaltiesV1._INTERFACE_ID_FEES) returns(bool result) {
                                    if (result) {
                                        return 3;
                                    }
                                } catch { }
                                
                                try IERC165Upgradeable(token).supportsInterface(LibRoyalties2981._INTERFACE_ID_ROYALTIES) returns(bool result) {
                                    if (result) {
                                        return 5;
                                    }
                                } catch { }
                                
                                if (royaltiesProvider != address(0)) {
                                    return 4;
                                }
                                if (royaltiesByToken[token].initialized) {
                                    return 1;
                                }
                                return 6;
                            }
                            /// @dev returns royalties for token contract and token id
                            function getRoyalties(address token, uint tokenId) override external returns (LibPart.Part[] memory) {
                                uint royaltiesProviderData = royaltiesProviders[token];
                                address royaltiesProvider = address(royaltiesProviderData);
                                uint royaltiesType = _getRoyaltiesType(royaltiesProviderData);
                                // case when royaltiesType is not set
                                if (royaltiesType == 0) {
                                    // calculating royalties type for token
                                    royaltiesType = calculateRoyaltiesType(token, royaltiesProvider);
                                    
                                    //saving royalties type
                                    setRoyaltiesType(token, royaltiesType, royaltiesProvider);
                                }
                                //case royaltiesType = 1, royalties are set in royaltiesByToken
                                if (royaltiesType == 1) {
                                    return royaltiesByToken[token].royalties;
                                }
                                //case royaltiesType = 2, royalties rarible v2
                                if (royaltiesType == 2) {
                                    return getRoyaltiesRaribleV2(token,tokenId);
                                }
                                //case royaltiesType = 3, royalties rarible v1
                                if (royaltiesType == 3) {
                                    return getRoyaltiesRaribleV1(token, tokenId);
                                }
                                //case royaltiesType = 4, royalties from external provider
                                if (royaltiesType == 4) {
                                    return providerExtractor(token, tokenId, royaltiesProvider);
                                }
                                //case royaltiesType = 5, royalties EIP-2981
                                if (royaltiesType == 5) {
                                    return getRoyaltiesEIP2981(token, tokenId);
                                }
                                // case royaltiesType = 6, unknown/empty royalties
                                if (royaltiesType == 6) {
                                    return new LibPart.Part[](0);
                                } 
                                revert("something wrong in getRoyalties");
                            }
                            /// @dev tries to get royalties rarible-v2 for token and tokenId
                            function getRoyaltiesRaribleV2(address token, uint tokenId) internal view returns (LibPart.Part[] memory) {
                                try RoyaltiesV2(token).getRaribleV2Royalties(tokenId) returns (LibPart.Part[] memory result) {
                                    return result;
                                } catch {
                                    return new LibPart.Part[](0);
                                }
                            }
                            /// @dev tries to get royalties rarible-v1 for token and tokenId
                            function getRoyaltiesRaribleV1(address token, uint tokenId) internal view returns (LibPart.Part[] memory) {
                                RoyaltiesV1 v1 = RoyaltiesV1(token);
                                address payable[] memory recipients;
                                try v1.getFeeRecipients(tokenId) returns (address payable[] memory resultRecipients) {
                                    recipients = resultRecipients;
                                } catch {
                                    return new LibPart.Part[](0);
                                }
                                uint[] memory values;
                                try v1.getFeeBps(tokenId) returns (uint[] memory resultValues) {
                                    values = resultValues;
                                } catch {
                                    return new LibPart.Part[](0);
                                }
                                if (values.length != recipients.length) {
                                    return new LibPart.Part[](0);
                                }
                                LibPart.Part[] memory result = new LibPart.Part[](values.length);
                                for (uint256 i = 0; i < values.length; i++) {
                                    result[i].value = uint96(values[i]);
                                    result[i].account = recipients[i];
                                }
                                return result;
                            }
                            /// @dev tries to get royalties EIP-2981 for token and tokenId
                            function getRoyaltiesEIP2981(address token, uint tokenId) internal view returns (LibPart.Part[] memory) {
                                try IERC2981(token).royaltyInfo(tokenId, LibRoyalties2981._WEIGHT_VALUE) returns (address receiver, uint256 royaltyAmount) {
                                    return LibRoyalties2981.calculateRoyalties(receiver, royaltyAmount);
                                } catch {
                                    return new LibPart.Part[](0);
                                }
                            }
                            /// @dev tries to get royalties for token and tokenId from external provider set in royaltiesProviders
                            function providerExtractor(address token, uint tokenId, address providerAddress) internal returns (LibPart.Part[] memory) {
                                try IRoyaltiesProvider(providerAddress).getRoyalties(token, tokenId) returns (LibPart.Part[] memory result) {
                                    return result;
                                } catch {
                                    return new LibPart.Part[](0);
                                }
                            }
                            uint256[46] private __gap;
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.2 <0.8.0;
                        pragma abicoder v2;
                        import "@rarible/lib-part/contracts/LibPart.sol";
                        interface IRoyaltiesProvider {
                            function getRoyalties(address token, uint tokenId) external returns (LibPart.Part[] memory);
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.2 <0.8.0;
                        library LibRoyaltiesV2 {
                            /*
                             * bytes4(keccak256('getRaribleV2Royalties(uint256)')) == 0xcad96cca
                             */
                            bytes4 constant _INTERFACE_ID_ROYALTIES = 0xcad96cca;
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.2 <0.8.0;
                        library LibRoyaltiesV1 {
                            /*
                             * bytes4(keccak256('getFeeBps(uint256)')) == 0x0ebd4c7f
                             * bytes4(keccak256('getFeeRecipients(uint256)')) == 0xb9c4d9fb
                             *
                             * => 0x0ebd4c7f ^ 0xb9c4d9fb == 0xb7799584
                             */
                            bytes4 constant _INTERFACE_ID_FEES = 0xb7799584;
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.2 <0.8.0;
                        import "@rarible/lib-part/contracts/LibPart.sol";
                        library LibRoyalties2981 {
                            /*
                             * https://eips.ethereum.org/EIPS/eip-2981: bytes4 private constant _INTERFACE_ID_ERC2981 = 0x2a55205a;
                             */
                            bytes4 constant _INTERFACE_ID_ROYALTIES = 0x2a55205a;
                            uint96 constant _WEIGHT_VALUE = 1000000;
                            /*Method for converting amount to percent and forming LibPart*/
                            function calculateRoyalties(address to, uint256 amount) internal view returns (LibPart.Part[] memory) {
                                LibPart.Part[] memory result;
                                if (amount == 0) {
                                    return result;
                                }
                                uint256 percent = amount * 10000 / _WEIGHT_VALUE;
                                require(percent < 10000, "Royalties 2981 exceeds 100%");
                                result = new LibPart.Part[](1);
                                result[0].account = payable(to);
                                result[0].value = uint96(percent);
                                return result;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.2 <0.8.0;
                        interface RoyaltiesV1 {
                            event SecondarySaleFees(uint256 tokenId, address[] recipients, uint[] bps);
                            function getFeeRecipients(uint256 id) external view returns (address payable[] memory);
                            function getFeeBps(uint256 id) external view returns (uint[] memory);
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.2 <0.8.0;
                        pragma abicoder v2;
                        import "@rarible/lib-part/contracts/LibPart.sol";
                        interface RoyaltiesV2 {
                            event RoyaltiesSet(uint256 tokenId, LibPart.Part[] royalties);
                            function getRaribleV2Royalties(uint256 id) external view returns (LibPart.Part[] memory);
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.2 <0.8.0;
                        import "@rarible/lib-part/contracts/LibPart.sol";
                        ///
                        /// @dev Interface for the NFT Royalty Standard
                        ///
                        //interface IERC2981 is IERC165 {
                        interface IERC2981 {
                            /// ERC165 bytes to add to interface array - set in parent contract
                            /// implementing this standard
                            ///
                            /// bytes4(keccak256("royaltyInfo(uint256,uint256)")) == 0x2a55205a
                            /// bytes4 private constant _INTERFACE_ID_ERC2981 = 0x2a55205a;
                            /// _registerInterface(_INTERFACE_ID_ERC2981);
                            /// @notice Called with the sale price to determine how much royalty
                            //          is owed and to whom.
                            /// @param _tokenId - the NFT asset queried for royalty information
                            /// @param _salePrice - the sale price of the NFT asset specified by _tokenId
                            /// @return receiver - address of who should be sent the royalty payment
                            /// @return royaltyAmount - the royalty payment amount for _salePrice
                            function royaltyInfo(
                                uint256 _tokenId,
                                uint256 _salePrice
                            ) external view returns (
                                address receiver,
                                uint256 royaltyAmount
                            );
                        }// SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.2 <0.8.0;
                        import "../../introspection/IERC165Upgradeable.sol";
                        /**
                         * @dev Required interface of an ERC721 compliant contract.
                         */
                        interface IERC721Upgradeable is IERC165Upgradeable {
                            /**
                             * @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
                        pragma solidity >=0.6.0 <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 IERC165Upgradeable {
                            /**
                             * @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
                        pragma solidity >=0.6.0 <0.8.0;
                        import "../utils/ContextUpgradeable.sol";
                        import "../proxy/Initializable.sol";
                        /**
                         * @dev Contract module which provides a basic access control mechanism, where
                         * there is an account (an owner) that can be granted exclusive access to
                         * specific functions.
                         *
                         * By default, the owner account will be the one that deploys the contract. This
                         * can later be changed with {transferOwnership}.
                         *
                         * This module is used through inheritance. It will make available the modifier
                         * `onlyOwner`, which can be applied to your functions to restrict their use to
                         * the owner.
                         */
                        abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
                            address private _owner;
                            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                            /**
                             * @dev Initializes the contract setting the deployer as the initial owner.
                             */
                            function __Ownable_init() internal initializer {
                                __Context_init_unchained();
                                __Ownable_init_unchained();
                            }
                            function __Ownable_init_unchained() internal initializer {
                                address msgSender = _msgSender();
                                _owner = msgSender;
                                emit OwnershipTransferred(address(0), msgSender);
                            }
                            /**
                             * @dev Returns the address of the current owner.
                             */
                            function owner() public view virtual returns (address) {
                                return _owner;
                            }
                            /**
                             * @dev Throws if called by any account other than the owner.
                             */
                            modifier onlyOwner() {
                                require(owner() == _msgSender(), "Ownable: caller is not the owner");
                                _;
                            }
                            /**
                             * @dev Leaves the contract without owner. It will not be possible to call
                             * `onlyOwner` functions anymore. Can only be called by the current owner.
                             *
                             * NOTE: Renouncing ownership will leave the contract without an owner,
                             * thereby removing any functionality that is only available to the owner.
                             */
                            function renounceOwnership() public virtual onlyOwner {
                                emit OwnershipTransferred(_owner, address(0));
                                _owner = address(0);
                            }
                            /**
                             * @dev Transfers ownership of the contract to a new account (`newOwner`).
                             * Can only be called by the current owner.
                             */
                            function transferOwnership(address newOwner) public virtual onlyOwner {
                                require(newOwner != address(0), "Ownable: new owner is the zero address");
                                emit OwnershipTransferred(_owner, newOwner);
                                _owner = newOwner;
                            }
                            uint256[49] private __gap;
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.0 <0.8.0;
                        import "../proxy/Initializable.sol";
                        /*
                         * @dev Provides information about the current execution context, including the
                         * sender of the transaction and its data. While these are generally available
                         * via msg.sender and msg.data, they should not be accessed in such a direct
                         * manner, since when dealing with GSN meta-transactions the account sending and
                         * paying for execution may not be the actual sender (as far as an application
                         * is concerned).
                         *
                         * This contract is only required for intermediate, library-like contracts.
                         */
                        abstract contract ContextUpgradeable is Initializable {
                            function __Context_init() internal initializer {
                                __Context_init_unchained();
                            }
                            function __Context_init_unchained() internal initializer {
                            }
                            function _msgSender() internal view virtual returns (address payable) {
                                return msg.sender;
                            }
                            function _msgData() internal view virtual returns (bytes memory) {
                                this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                                return msg.data;
                            }
                            uint256[50] private __gap;
                        }
                        // SPDX-License-Identifier: MIT
                        // solhint-disable-next-line compiler-version
                        pragma solidity >=0.4.24 <0.8.0;
                        import "../utils/AddressUpgradeable.sol";
                        /**
                         * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
                         * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
                         * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
                         * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
                         *
                         * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
                         * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}.
                         *
                         * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
                         * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
                         */
                        abstract contract Initializable {
                            /**
                             * @dev Indicates that the contract has been initialized.
                             */
                            bool private _initialized;
                            /**
                             * @dev Indicates that the contract is in the process of being initialized.
                             */
                            bool private _initializing;
                            /**
                             * @dev Modifier to protect an initializer function from being invoked twice.
                             */
                            modifier initializer() {
                                require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized");
                                bool isTopLevelCall = !_initializing;
                                if (isTopLevelCall) {
                                    _initializing = true;
                                    _initialized = true;
                                }
                                _;
                                if (isTopLevelCall) {
                                    _initializing = false;
                                }
                            }
                            /// @dev Returns true if and only if the function is running in the constructor
                            function _isConstructor() private view returns (bool) {
                                return !AddressUpgradeable.isContract(address(this));
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.2 <0.8.0;
                        /**
                         * @dev Collection of functions related to the address type
                         */
                        library AddressUpgradeable {
                            /**
                             * @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;
                                // solhint-disable-next-line no-inline-assembly
                                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");
                                // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                                (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");
                                // solhint-disable-next-line avoid-low-level-calls
                                (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");
                                // solhint-disable-next-line avoid-low-level-calls
                                (bool success, bytes memory returndata) = target.staticcall(data);
                                return _verifyCallResult(success, returndata, errorMessage);
                            }
                            function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private 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
                                        // solhint-disable-next-line no-inline-assembly
                                        assembly {
                                            let returndata_size := mload(returndata)
                                            revert(add(32, returndata), returndata_size)
                                        }
                                    } else {
                                        revert(errorMessage);
                                    }
                                }
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity >=0.6.2 <0.8.0;
                        library LibPart {
                            bytes32 public constant TYPE_HASH = keccak256("Part(address account,uint96 value)");
                            struct Part {
                                address payable account;
                                uint96 value;
                            }
                            function hash(Part memory part) internal pure returns (bytes32) {
                                return keccak256(abi.encode(TYPE_HASH, part.account, part.value));
                            }
                        }
                        

                        File 10 of 11: DropERC721
                        // SPDX-License-Identifier: MIT
                        // ERC721A Contracts v3.3.0
                        // Creator: Chiru Labs
                        ////////// CHANGELOG: turn `approve` to virtual //////////
                        pragma solidity ^0.8.4;
                        import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol";
                        import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol";
                        import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
                        import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
                        import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";
                        import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol";
                        import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
                        /**
                         * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
                         * the Metadata extension. Built to optimize for lower gas during batch mints.
                         *
                         * Assumes serials are sequentially minted starting at _startTokenId() (defaults to 0, e.g. 0, 1, 2, 3..).
                         *
                         * Assumes that an owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
                         *
                         * Assumes that the maximum token id cannot exceed 2**256 - 1 (max value of uint256).
                         */
                        contract ERC721AUpgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721AUpgradeable {
                            using AddressUpgradeable for address;
                            using StringsUpgradeable for uint256;
                            // The tokenId of the next token to be minted.
                            uint256 internal _currentIndex;
                            // The number of tokens burned.
                            uint256 internal _burnCounter;
                            // Token name
                            string private _name;
                            // Token symbol
                            string private _symbol;
                            // Mapping from token ID to ownership details
                            // An empty struct value does not necessarily mean the token is unowned. See _ownershipOf implementation for details.
                            mapping(uint256 => TokenOwnership) internal _ownerships;
                            // Mapping owner address to address data
                            mapping(address => AddressData) private _addressData;
                            // Mapping from token ID to approved address
                            mapping(uint256 => address) private _tokenApprovals;
                            // Mapping from owner to operator approvals
                            mapping(address => mapping(address => bool)) private _operatorApprovals;
                            function __ERC721A_init(string memory name_, string memory symbol_) internal onlyInitializing {
                                __ERC721A_init_unchained(name_, symbol_);
                            }
                            function __ERC721A_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
                                _name = name_;
                                _symbol = symbol_;
                                _currentIndex = _startTokenId();
                            }
                            /**
                             * To change the starting tokenId, please override this function.
                             */
                            function _startTokenId() internal view virtual returns (uint256) {
                                return 0;
                            }
                            /**
                             * @dev Burned tokens are calculated here, use _totalMinted() if you want to count just minted tokens.
                             */
                            function totalSupply() public view override returns (uint256) {
                                // Counter underflow is impossible as _burnCounter cannot be incremented
                                // more than _currentIndex - _startTokenId() times
                                unchecked {
                                    return _currentIndex - _burnCounter - _startTokenId();
                                }
                            }
                            /**
                             * Returns the total amount of tokens minted in the contract.
                             */
                            function _totalMinted() internal view returns (uint256) {
                                // Counter underflow is impossible as _currentIndex does not decrement,
                                // and it is initialized to _startTokenId()
                                unchecked {
                                    return _currentIndex - _startTokenId();
                                }
                            }
                            /**
                             * @dev See {IERC165-supportsInterface}.
                             */
                            function supportsInterface(
                                bytes4 interfaceId
                            ) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) {
                                return
                                    interfaceId == type(IERC721Upgradeable).interfaceId ||
                                    interfaceId == type(IERC721MetadataUpgradeable).interfaceId ||
                                    super.supportsInterface(interfaceId);
                            }
                            /**
                             * @dev See {IERC721-balanceOf}.
                             */
                            function balanceOf(address owner) public view override returns (uint256) {
                                if (owner == address(0)) revert BalanceQueryForZeroAddress();
                                return uint256(_addressData[owner].balance);
                            }
                            /**
                             * Returns the number of tokens minted by `owner`.
                             */
                            function _numberMinted(address owner) internal view returns (uint256) {
                                return uint256(_addressData[owner].numberMinted);
                            }
                            /**
                             * Returns the number of tokens burned by or on behalf of `owner`.
                             */
                            function _numberBurned(address owner) internal view returns (uint256) {
                                return uint256(_addressData[owner].numberBurned);
                            }
                            /**
                             * Returns the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
                             */
                            function _getAux(address owner) internal view returns (uint64) {
                                return _addressData[owner].aux;
                            }
                            /**
                             * Sets the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
                             * If there are multiple variables, please pack them into a uint64.
                             */
                            function _setAux(address owner, uint64 aux) internal {
                                _addressData[owner].aux = aux;
                            }
                            /**
                             * Gas spent here starts off proportional to the maximum mint batch size.
                             * It gradually moves to O(1) as tokens get transferred around in the collection over time.
                             */
                            function _ownershipOf(uint256 tokenId) internal view returns (TokenOwnership memory) {
                                uint256 curr = tokenId;
                                unchecked {
                                    if (_startTokenId() <= curr)
                                        if (curr < _currentIndex) {
                                            TokenOwnership memory ownership = _ownerships[curr];
                                            if (!ownership.burned) {
                                                if (ownership.addr != address(0)) {
                                                    return ownership;
                                                }
                                                // Invariant:
                                                // There will always be an ownership that has an address and is not burned
                                                // before an ownership that does not have an address and is not burned.
                                                // Hence, curr will not underflow.
                                                while (true) {
                                                    curr--;
                                                    ownership = _ownerships[curr];
                                                    if (ownership.addr != address(0)) {
                                                        return ownership;
                                                    }
                                                }
                                            }
                                        }
                                }
                                revert OwnerQueryForNonexistentToken();
                            }
                            /**
                             * @dev See {IERC721-ownerOf}.
                             */
                            function ownerOf(uint256 tokenId) public view override returns (address) {
                                return _ownershipOf(tokenId).addr;
                            }
                            /**
                             * @dev See {IERC721Metadata-name}.
                             */
                            function name() public view virtual override returns (string memory) {
                                return _name;
                            }
                            /**
                             * @dev See {IERC721Metadata-symbol}.
                             */
                            function symbol() public view virtual override returns (string memory) {
                                return _symbol;
                            }
                            /**
                             * @dev See {IERC721Metadata-tokenURI}.
                             */
                            function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
                                if (!_exists(tokenId)) revert URIQueryForNonexistentToken();
                                string memory baseURI = _baseURI();
                                return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
                            }
                            /**
                             * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
                             * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
                             * by default, can be overriden in child contracts.
                             */
                            function _baseURI() internal view virtual returns (string memory) {
                                return "";
                            }
                            /**
                             * @dev See {IERC721-approve}.
                             */
                            function approve(address to, uint256 tokenId) public virtual override {
                                address owner = ERC721AUpgradeable.ownerOf(tokenId);
                                if (to == owner) revert ApprovalToCurrentOwner();
                                if (_msgSender() != owner)
                                    if (!isApprovedForAll(owner, _msgSender())) {
                                        revert ApprovalCallerNotOwnerNorApproved();
                                    }
                                _approve(to, tokenId, owner);
                            }
                            /**
                             * @dev See {IERC721-getApproved}.
                             */
                            function getApproved(uint256 tokenId) public view override returns (address) {
                                if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();
                                return _tokenApprovals[tokenId];
                            }
                            /**
                             * @dev See {IERC721-setApprovalForAll}.
                             */
                            function setApprovalForAll(address operator, bool approved) public virtual override {
                                if (operator == _msgSender()) revert ApproveToCaller();
                                _operatorApprovals[_msgSender()][operator] = approved;
                                emit ApprovalForAll(_msgSender(), operator, approved);
                            }
                            /**
                             * @dev See {IERC721-isApprovedForAll}.
                             */
                            function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
                                return _operatorApprovals[owner][operator];
                            }
                            /**
                             * @dev See {IERC721-transferFrom}.
                             */
                            function transferFrom(address from, address to, uint256 tokenId) public virtual override {
                                _transfer(from, to, tokenId);
                            }
                            /**
                             * @dev See {IERC721-safeTransferFrom}.
                             */
                            function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
                                safeTransferFrom(from, to, tokenId, "");
                            }
                            /**
                             * @dev See {IERC721-safeTransferFrom}.
                             */
                            function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override {
                                _transfer(from, to, tokenId);
                                if (to.isContract())
                                    if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
                                        revert TransferToNonERC721ReceiverImplementer();
                                    }
                            }
                            /**
                             * @dev Returns whether `tokenId` exists.
                             *
                             * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
                             *
                             * Tokens start existing when they are minted (`_mint`),
                             */
                            function _exists(uint256 tokenId) internal view returns (bool) {
                                return _startTokenId() <= tokenId && tokenId < _currentIndex && !_ownerships[tokenId].burned;
                            }
                            /**
                             * @dev Equivalent to `_safeMint(to, quantity, '')`.
                             */
                            function _safeMint(address to, uint256 quantity) internal {
                                _safeMint(to, quantity, "");
                            }
                            /**
                             * @dev Safely mints `quantity` tokens and transfers them to `to`.
                             *
                             * Requirements:
                             *
                             * - If `to` refers to a smart contract, it must implement
                             *   {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
                             * - `quantity` must be greater than 0.
                             *
                             * Emits a {Transfer} event.
                             */
                            function _safeMint(address to, uint256 quantity, bytes memory _data) internal {
                                uint256 startTokenId = _currentIndex;
                                if (to == address(0)) revert MintToZeroAddress();
                                if (quantity == 0) revert MintZeroQuantity();
                                _beforeTokenTransfers(address(0), to, startTokenId, quantity);
                                // Overflows are incredibly unrealistic.
                                // balance or numberMinted overflow if current value of either + quantity > 1.8e19 (2**64) - 1
                                // updatedIndex overflows if _currentIndex + quantity > 1.2e77 (2**256) - 1
                                unchecked {
                                    _addressData[to].balance += uint64(quantity);
                                    _addressData[to].numberMinted += uint64(quantity);
                                    _ownerships[startTokenId].addr = to;
                                    _ownerships[startTokenId].startTimestamp = uint64(block.timestamp);
                                    uint256 updatedIndex = startTokenId;
                                    uint256 end = updatedIndex + quantity;
                                    if (to.isContract()) {
                                        do {
                                            emit Transfer(address(0), to, updatedIndex);
                                            if (!_checkContractOnERC721Received(address(0), to, updatedIndex, _data)) {
                                                revert TransferToNonERC721ReceiverImplementer();
                                            }
                                            updatedIndex += 1;
                                        } while (updatedIndex < end);
                                        // Reentrancy protection
                                        if (_currentIndex != startTokenId) revert();
                                    } else {
                                        do {
                                            emit Transfer(address(0), to, updatedIndex++);
                                        } while (updatedIndex < end);
                                    }
                                    _currentIndex = updatedIndex;
                                }
                                _afterTokenTransfers(address(0), to, startTokenId, quantity);
                            }
                            /**
                             * @dev Mints `quantity` tokens and transfers them to `to`.
                             *
                             * Requirements:
                             *
                             * - `to` cannot be the zero address.
                             * - `quantity` must be greater than 0.
                             *
                             * Emits a {Transfer} event.
                             */
                            function _mint(address to, uint256 quantity) internal {
                                uint256 startTokenId = _currentIndex;
                                if (to == address(0)) revert MintToZeroAddress();
                                if (quantity == 0) revert MintZeroQuantity();
                                _beforeTokenTransfers(address(0), to, startTokenId, quantity);
                                // Overflows are incredibly unrealistic.
                                // balance or numberMinted overflow if current value of either + quantity > 1.8e19 (2**64) - 1
                                // updatedIndex overflows if _currentIndex + quantity > 1.2e77 (2**256) - 1
                                unchecked {
                                    _addressData[to].balance += uint64(quantity);
                                    _addressData[to].numberMinted += uint64(quantity);
                                    _ownerships[startTokenId].addr = to;
                                    _ownerships[startTokenId].startTimestamp = uint64(block.timestamp);
                                    uint256 updatedIndex = startTokenId;
                                    uint256 end = updatedIndex + quantity;
                                    do {
                                        emit Transfer(address(0), to, updatedIndex++);
                                    } while (updatedIndex < end);
                                    _currentIndex = updatedIndex;
                                }
                                _afterTokenTransfers(address(0), to, startTokenId, quantity);
                            }
                            /**
                             * @dev Transfers `tokenId` from `from` to `to`.
                             *
                             * Requirements:
                             *
                             * - `to` cannot be the zero address.
                             * - `tokenId` token must be owned by `from`.
                             *
                             * Emits a {Transfer} event.
                             */
                            function _transfer(address from, address to, uint256 tokenId) private {
                                TokenOwnership memory prevOwnership = _ownershipOf(tokenId);
                                if (prevOwnership.addr != from) revert TransferFromIncorrectOwner();
                                bool isApprovedOrOwner = (_msgSender() == from ||
                                    isApprovedForAll(from, _msgSender()) ||
                                    getApproved(tokenId) == _msgSender());
                                if (!isApprovedOrOwner) revert TransferCallerNotOwnerNorApproved();
                                if (to == address(0)) revert TransferToZeroAddress();
                                _beforeTokenTransfers(from, to, tokenId, 1);
                                // Clear approvals from the previous owner
                                _approve(address(0), tokenId, from);
                                // Underflow of the sender's balance is impossible because we check for
                                // ownership above and the recipient's balance can't realistically overflow.
                                // Counter overflow is incredibly unrealistic as tokenId would have to be 2**256.
                                unchecked {
                                    _addressData[from].balance -= 1;
                                    _addressData[to].balance += 1;
                                    TokenOwnership storage currSlot = _ownerships[tokenId];
                                    currSlot.addr = to;
                                    currSlot.startTimestamp = uint64(block.timestamp);
                                    // If the ownership slot of tokenId+1 is not explicitly set, that means the transfer initiator owns it.
                                    // Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.
                                    uint256 nextTokenId = tokenId + 1;
                                    TokenOwnership storage nextSlot = _ownerships[nextTokenId];
                                    if (nextSlot.addr == address(0)) {
                                        // This will suffice for checking _exists(nextTokenId),
                                        // as a burned slot cannot contain the zero address.
                                        if (nextTokenId != _currentIndex) {
                                            nextSlot.addr = from;
                                            nextSlot.startTimestamp = prevOwnership.startTimestamp;
                                        }
                                    }
                                }
                                emit Transfer(from, to, tokenId);
                                _afterTokenTransfers(from, to, tokenId, 1);
                            }
                            /**
                             * @dev Equivalent to `_burn(tokenId, false)`.
                             */
                            function _burn(uint256 tokenId) internal virtual {
                                _burn(tokenId, false);
                            }
                            /**
                             * @dev Destroys `tokenId`.
                             * The approval is cleared when the token is burned.
                             *
                             * Requirements:
                             *
                             * - `tokenId` must exist.
                             *
                             * Emits a {Transfer} event.
                             */
                            function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
                                TokenOwnership memory prevOwnership = _ownershipOf(tokenId);
                                address from = prevOwnership.addr;
                                if (approvalCheck) {
                                    bool isApprovedOrOwner = (_msgSender() == from ||
                                        isApprovedForAll(from, _msgSender()) ||
                                        getApproved(tokenId) == _msgSender());
                                    if (!isApprovedOrOwner) revert TransferCallerNotOwnerNorApproved();
                                }
                                _beforeTokenTransfers(from, address(0), tokenId, 1);
                                // Clear approvals from the previous owner
                                _approve(address(0), tokenId, from);
                                // Underflow of the sender's balance is impossible because we check for
                                // ownership above and the recipient's balance can't realistically overflow.
                                // Counter overflow is incredibly unrealistic as tokenId would have to be 2**256.
                                unchecked {
                                    AddressData storage addressData = _addressData[from];
                                    addressData.balance -= 1;
                                    addressData.numberBurned += 1;
                                    // Keep track of who burned the token, and the timestamp of burning.
                                    TokenOwnership storage currSlot = _ownerships[tokenId];
                                    currSlot.addr = from;
                                    currSlot.startTimestamp = uint64(block.timestamp);
                                    currSlot.burned = true;
                                    // If the ownership slot of tokenId+1 is not explicitly set, that means the burn initiator owns it.
                                    // Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.
                                    uint256 nextTokenId = tokenId + 1;
                                    TokenOwnership storage nextSlot = _ownerships[nextTokenId];
                                    if (nextSlot.addr == address(0)) {
                                        // This will suffice for checking _exists(nextTokenId),
                                        // as a burned slot cannot contain the zero address.
                                        if (nextTokenId != _currentIndex) {
                                            nextSlot.addr = from;
                                            nextSlot.startTimestamp = prevOwnership.startTimestamp;
                                        }
                                    }
                                }
                                emit Transfer(from, address(0), tokenId);
                                _afterTokenTransfers(from, address(0), tokenId, 1);
                                // Overflow not possible, as _burnCounter cannot be exceed _currentIndex times.
                                unchecked {
                                    _burnCounter++;
                                }
                            }
                            /**
                             * @dev Approve `to` to operate on `tokenId`
                             *
                             * Emits a {Approval} event.
                             */
                            function _approve(address to, uint256 tokenId, address owner) private {
                                _tokenApprovals[tokenId] = to;
                                emit Approval(owner, to, tokenId);
                            }
                            /**
                             * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target contract.
                             *
                             * @param from address representing the previous owner of the given token ID
                             * @param to target address that will receive the tokens
                             * @param tokenId uint256 ID of the token to be transferred
                             * @param _data bytes optional data to send along with the call
                             * @return bool whether the call correctly returned the expected magic value
                             */
                            function _checkContractOnERC721Received(
                                address from,
                                address to,
                                uint256 tokenId,
                                bytes memory _data
                            ) private returns (bool) {
                                try IERC721ReceiverUpgradeable(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (
                                    bytes4 retval
                                ) {
                                    return retval == IERC721ReceiverUpgradeable(to).onERC721Received.selector;
                                } catch (bytes memory reason) {
                                    if (reason.length == 0) {
                                        revert TransferToNonERC721ReceiverImplementer();
                                    } else {
                                        assembly {
                                            revert(add(32, reason), mload(reason))
                                        }
                                    }
                                }
                            }
                            /**
                             * @dev Hook that is called before a set of serially-ordered token ids are about to be transferred. This includes minting.
                             * And also called before burning one token.
                             *
                             * startTokenId - the first token id to be transferred
                             * quantity - the amount to be transferred
                             *
                             * Calling conditions:
                             *
                             * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
                             * transferred to `to`.
                             * - When `from` is zero, `tokenId` will be minted for `to`.
                             * - When `to` is zero, `tokenId` will be burned by `from`.
                             * - `from` and `to` are never both zero.
                             */
                            function _beforeTokenTransfers(address from, address to, uint256 startTokenId, uint256 quantity) internal virtual {}
                            /**
                             * @dev Hook that is called after a set of serially-ordered token ids have been transferred. This includes
                             * minting.
                             * And also called after one token has been burned.
                             *
                             * startTokenId - the first token id to be transferred
                             * quantity - the amount to be transferred
                             *
                             * Calling conditions:
                             *
                             * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
                             * transferred to `to`.
                             * - When `from` is zero, `tokenId` has been minted for `to`.
                             * - When `to` is zero, `tokenId` has been burned by `from`.
                             * - `from` and `to` are never both zero.
                             */
                            function _afterTokenTransfers(address from, address to, uint256 startTokenId, uint256 quantity) internal virtual {}
                            /**
                             * @dev This empty reserved space is put in place to allow future versions to add new
                             * variables without shifting down storage in the inheritance chain.
                             * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                             */
                            uint256[42] private __gap;
                        }
                        // 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
                         * [EIP](https://eips.ethereum.org/EIPS/eip-165).
                         *
                         * 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
                             * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
                             * 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: Apache-2.0
                        pragma solidity ^0.8.0;
                        /**
                         * @title ERC20 interface
                         * @dev see https://github.com/ethereum/EIPs/issues/20
                         */
                        interface IERC20 {
                            function totalSupply() external view returns (uint256);
                            function balanceOf(address who) external view returns (uint256);
                            function allowance(address owner, address spender) external view returns (uint256);
                            function transfer(address to, uint256 value) external returns (bool);
                            function approve(address spender, uint256 value) external returns (bool);
                            function transferFrom(address from, address to, uint256 value) external returns (bool);
                            event Transfer(address indexed from, address indexed to, uint256 value);
                            event Approval(address indexed owner, address indexed spender, uint256 value);
                        }
                        // SPDX-License-Identifier: Apache 2.0
                        pragma solidity ^0.8.0;
                        import "./IERC165.sol";
                        /**
                         * @dev Interface for the NFT Royalty Standard.
                         *
                         * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
                         * support for royalty payments across all NFT marketplaces and ecosystem participants.
                         *
                         * _Available since v4.5._
                         */
                        interface IERC2981 is IERC165 {
                            /**
                             * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
                             * exchange. The royalty amount is denominated and should be payed in that same unit of exchange.
                             */
                            function royaltyInfo(
                                uint256 tokenId,
                                uint256 salePrice
                            ) external view returns (address receiver, uint256 royaltyAmount);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        /**
                         *  @title   Batch-mint Metadata
                         *  @notice  The `BatchMintMetadata` is a contract extension for any base NFT contract. It lets the smart contract
                         *           using this extension set metadata for `n` number of NFTs all at once. This is enabled by storing a single
                         *           base URI for a batch of `n` NFTs, where the metadata for each NFT in a relevant batch is `baseURI/tokenId`.
                         */
                        contract BatchMintMetadata {
                            /// @dev Invalid index for batch
                            error BatchMintInvalidBatchId(uint256 index);
                            /// @dev Invalid token
                            error BatchMintInvalidTokenId(uint256 tokenId);
                            /// @dev Metadata frozen
                            error BatchMintMetadataFrozen(uint256 batchId);
                            /// @dev Largest tokenId of each batch of tokens with the same baseURI + 1 {ex: batchId 100 at position 0 includes tokens 0-99}
                            uint256[] private batchIds;
                            /// @dev Mapping from id of a batch of tokens => to base URI for the respective batch of tokens.
                            mapping(uint256 => string) private baseURI;
                            /// @dev Mapping from id of a batch of tokens => to whether the base URI for the respective batch of tokens is frozen.
                            mapping(uint256 => bool) public batchFrozen;
                            /// @dev This event emits when the metadata of all tokens are frozen.
                            /// While not currently supported by marketplaces, this event allows
                            /// future indexing if desired.
                            event MetadataFrozen();
                            // @dev This event emits when the metadata of a range of tokens is updated.
                            /// So that the third-party platforms such as NFT market could
                            /// timely update the images and related attributes of the NFTs.
                            event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
                            /**
                             *  @notice         Returns the count of batches of NFTs.
                             *  @dev            Each batch of tokens has an in ID and an associated `baseURI`.
                             *                  See {batchIds}.
                             */
                            function getBaseURICount() public view returns (uint256) {
                                return batchIds.length;
                            }
                            /**
                             *  @notice         Returns the ID for the batch of tokens at the given index.
                             *  @dev            See {getBaseURICount}.
                             *  @param _index   Index of the desired batch in batchIds array.
                             */
                            function getBatchIdAtIndex(uint256 _index) public view returns (uint256) {
                                if (_index >= getBaseURICount()) {
                                    revert BatchMintInvalidBatchId(_index);
                                }
                                return batchIds[_index];
                            }
                            /// @dev Returns the id for the batch of tokens the given tokenId belongs to.
                            function _getBatchId(uint256 _tokenId) internal view returns (uint256 batchId, uint256 index) {
                                uint256 numOfTokenBatches = getBaseURICount();
                                uint256[] memory indices = batchIds;
                                for (uint256 i = 0; i < numOfTokenBatches; i += 1) {
                                    if (_tokenId < indices[i]) {
                                        index = i;
                                        batchId = indices[i];
                                        return (batchId, index);
                                    }
                                }
                                revert BatchMintInvalidTokenId(_tokenId);
                            }
                            /// @dev Returns the baseURI for a token. The intended metadata URI for the token is baseURI + tokenId.
                            function _getBaseURI(uint256 _tokenId) internal view returns (string memory) {
                                uint256 numOfTokenBatches = getBaseURICount();
                                uint256[] memory indices = batchIds;
                                for (uint256 i = 0; i < numOfTokenBatches; i += 1) {
                                    if (_tokenId < indices[i]) {
                                        return baseURI[indices[i]];
                                    }
                                }
                                revert BatchMintInvalidTokenId(_tokenId);
                            }
                            /// @dev returns the starting tokenId of a given batchId.
                            function _getBatchStartId(uint256 _batchID) internal view returns (uint256) {
                                uint256 numOfTokenBatches = getBaseURICount();
                                uint256[] memory indices = batchIds;
                                for (uint256 i = 0; i < numOfTokenBatches; i++) {
                                    if (_batchID == indices[i]) {
                                        if (i > 0) {
                                            return indices[i - 1];
                                        }
                                        return 0;
                                    }
                                }
                                revert BatchMintInvalidBatchId(_batchID);
                            }
                            /// @dev Sets the base URI for the batch of tokens with the given batchId.
                            function _setBaseURI(uint256 _batchId, string memory _baseURI) internal {
                                if (batchFrozen[_batchId]) {
                                    revert BatchMintMetadataFrozen(_batchId);
                                }
                                baseURI[_batchId] = _baseURI;
                                emit BatchMetadataUpdate(_getBatchStartId(_batchId), _batchId);
                            }
                            /// @dev Freezes the base URI for the batch of tokens with the given batchId.
                            function _freezeBaseURI(uint256 _batchId) internal {
                                string memory baseURIForBatch = baseURI[_batchId];
                                if (bytes(baseURIForBatch).length == 0) {
                                    revert BatchMintInvalidBatchId(_batchId);
                                }
                                batchFrozen[_batchId] = true;
                                emit MetadataFrozen();
                            }
                            /// @dev Mints a batch of tokenIds and associates a common baseURI to all those Ids.
                            function _batchMintMetadata(
                                uint256 _startId,
                                uint256 _amountToMint,
                                string memory _baseURIForTokens
                            ) internal returns (uint256 nextTokenIdToMint, uint256 batchId) {
                                batchId = _startId + _amountToMint;
                                nextTokenIdToMint = batchId;
                                batchIds.push(batchId);
                                baseURI[batchId] = _baseURIForTokens;
                            }
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./interface/IContractMetadata.sol";
                        /**
                         *  @title   Contract Metadata
                         *  @notice  Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
                         *           for you contract.
                         *           Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
                         */
                        abstract contract ContractMetadata is IContractMetadata {
                            /// @dev The sender is not authorized to perform the action
                            error ContractMetadataUnauthorized();
                            /// @notice Returns the contract metadata URI.
                            string public override contractURI;
                            /**
                             *  @notice         Lets a contract admin set the URI for contract-level metadata.
                             *  @dev            Caller should be authorized to setup contractURI, e.g. contract admin.
                             *                  See {_canSetContractURI}.
                             *                  Emits {ContractURIUpdated Event}.
                             *
                             *  @param _uri     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                             */
                            function setContractURI(string memory _uri) external override {
                                if (!_canSetContractURI()) {
                                    revert ContractMetadataUnauthorized();
                                }
                                _setupContractURI(_uri);
                            }
                            /// @dev Lets a contract admin set the URI for contract-level metadata.
                            function _setupContractURI(string memory _uri) internal {
                                string memory prevURI = contractURI;
                                contractURI = _uri;
                                emit ContractURIUpdated(prevURI, _uri);
                            }
                            /// @dev Returns whether contract metadata can be set in the given execution context.
                            function _canSetContractURI() internal view virtual returns (bool);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./interface/IDelayedReveal.sol";
                        /**
                         *  @title   Delayed Reveal
                         *  @notice  Thirdweb's `DelayedReveal` is a contract extension for base NFT contracts. It lets you create batches of
                         *           'delayed-reveal' NFTs. You can learn more about the usage of delayed reveal NFTs here - https://blog.thirdweb.com/delayed-reveal-nfts
                         */
                        abstract contract DelayedReveal is IDelayedReveal {
                            /// @dev The contract doesn't have any url to be delayed revealed
                            error DelayedRevealNothingToReveal();
                            /// @dev The result of the returned an incorrect hash
                            error DelayedRevealIncorrectResultHash(bytes32 expected, bytes32 actual);
                            /// @dev Mapping from tokenId of a batch of tokens => to delayed reveal data.
                            mapping(uint256 => bytes) public encryptedData;
                            /// @dev Sets the delayed reveal data for a batchId.
                            function _setEncryptedData(uint256 _batchId, bytes memory _encryptedData) internal {
                                encryptedData[_batchId] = _encryptedData;
                            }
                            /**
                             *  @notice             Returns revealed URI for a batch of NFTs.
                             *  @dev                Reveal encrypted base URI for `_batchId` with caller/admin's `_key` used for encryption.
                             *                      Reverts if there's no encrypted URI for `_batchId`.
                             *                      See {encryptDecrypt}.
                             *
                             *  @param _batchId     ID of the batch for which URI is being revealed.
                             *  @param _key         Secure key used by caller/admin for encryption of baseURI.
                             *
                             *  @return revealedURI Decrypted base URI.
                             */
                            function getRevealURI(uint256 _batchId, bytes calldata _key) public view returns (string memory revealedURI) {
                                bytes memory data = encryptedData[_batchId];
                                if (data.length == 0) {
                                    revert DelayedRevealNothingToReveal();
                                }
                                (bytes memory encryptedURI, bytes32 provenanceHash) = abi.decode(data, (bytes, bytes32));
                                revealedURI = string(encryptDecrypt(encryptedURI, _key));
                                if (keccak256(abi.encodePacked(revealedURI, _key, block.chainid)) != provenanceHash) {
                                    revert DelayedRevealIncorrectResultHash(
                                        provenanceHash,
                                        keccak256(abi.encodePacked(revealedURI, _key, block.chainid))
                                    );
                                }
                            }
                            /**
                             *  @notice         Encrypt/decrypt data on chain.
                             *  @dev            Encrypt/decrypt given `data` with `key`. Uses inline assembly.
                             *                  See: https://ethereum.stackexchange.com/questions/69825/decrypt-message-on-chain
                             *
                             *  @param data     Bytes of data to encrypt/decrypt.
                             *  @param key      Secure key used by caller for encryption/decryption.
                             *
                             *  @return result  Output after encryption/decryption of given data.
                             */
                            function encryptDecrypt(bytes memory data, bytes calldata key) public pure override returns (bytes memory result) {
                                // Store data length on stack for later use
                                uint256 length = data.length;
                                // solhint-disable-next-line no-inline-assembly
                                assembly {
                                    // Set result to free memory pointer
                                    result := mload(0x40)
                                    // Increase free memory pointer by lenght + 32
                                    mstore(0x40, add(add(result, length), 32))
                                    // Set result length
                                    mstore(result, length)
                                }
                                // Iterate over the data stepping by 32 bytes
                                for (uint256 i = 0; i < length; i += 32) {
                                    // Generate hash of the key and offset
                                    bytes32 hash = keccak256(abi.encodePacked(key, i));
                                    bytes32 chunk;
                                    // solhint-disable-next-line no-inline-assembly
                                    assembly {
                                        // Read 32-bytes data chunk
                                        chunk := mload(add(data, add(i, 32)))
                                    }
                                    // XOR the chunk with hash
                                    chunk ^= hash;
                                    // solhint-disable-next-line no-inline-assembly
                                    assembly {
                                        // Write 32-byte encrypted chunk
                                        mstore(add(result, add(i, 32)), chunk)
                                    }
                                }
                            }
                            /**
                             *  @notice         Returns whether the relvant batch of NFTs is subject to a delayed reveal.
                             *  @dev            Returns `true` if `_batchId`'s base URI is encrypted.
                             *  @param _batchId ID of a batch of NFTs.
                             */
                            function isEncryptedBatch(uint256 _batchId) public view returns (bool) {
                                return encryptedData[_batchId].length > 0;
                            }
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./interface/IDrop.sol";
                        import "../lib/MerkleProof.sol";
                        abstract contract Drop is IDrop {
                            /// @dev The sender is not authorized to perform the action
                            error DropUnauthorized();
                            /// @dev Exceeded the max token total supply
                            error DropExceedMaxSupply();
                            /// @dev No active claim condition
                            error DropNoActiveCondition();
                            /// @dev Claim condition invalid currency or price
                            error DropClaimInvalidTokenPrice(
                                address expectedCurrency,
                                uint256 expectedPricePerToken,
                                address actualCurrency,
                                uint256 actualExpectedPricePerToken
                            );
                            /// @dev Claim condition exceeded limit
                            error DropClaimExceedLimit(uint256 expected, uint256 actual);
                            /// @dev Claim condition exceeded max supply
                            error DropClaimExceedMaxSupply(uint256 expected, uint256 actual);
                            /// @dev Claim condition not started yet
                            error DropClaimNotStarted(uint256 expected, uint256 actual);
                            /*///////////////////////////////////////////////////////////////
                                                    State variables
                            //////////////////////////////////////////////////////////////*/
                            /// @dev The active conditions for claiming tokens.
                            ClaimConditionList public claimCondition;
                            /*///////////////////////////////////////////////////////////////
                                                    Drop logic
                            //////////////////////////////////////////////////////////////*/
                            /// @dev Lets an account claim tokens.
                            function claim(
                                address _receiver,
                                uint256 _quantity,
                                address _currency,
                                uint256 _pricePerToken,
                                AllowlistProof calldata _allowlistProof,
                                bytes memory _data
                            ) public payable virtual override {
                                _beforeClaim(_receiver, _quantity, _currency, _pricePerToken, _allowlistProof, _data);
                                uint256 activeConditionId = getActiveClaimConditionId();
                                verifyClaim(activeConditionId, _dropMsgSender(), _quantity, _currency, _pricePerToken, _allowlistProof);
                                // Update contract state.
                                claimCondition.conditions[activeConditionId].supplyClaimed += _quantity;
                                claimCondition.supplyClaimedByWallet[activeConditionId][_dropMsgSender()] += _quantity;
                                // If there's a price, collect price.
                                _collectPriceOnClaim(address(0), _quantity, _currency, _pricePerToken);
                                // Mint the relevant tokens to claimer.
                                uint256 startTokenId = _transferTokensOnClaim(_receiver, _quantity);
                                emit TokensClaimed(activeConditionId, _dropMsgSender(), _receiver, startTokenId, _quantity);
                                _afterClaim(_receiver, _quantity, _currency, _pricePerToken, _allowlistProof, _data);
                            }
                            /// @dev Lets a contract admin set claim conditions.
                            function setClaimConditions(
                                ClaimCondition[] calldata _conditions,
                                bool _resetClaimEligibility
                            ) external virtual override {
                                if (!_canSetClaimConditions()) {
                                    revert DropUnauthorized();
                                }
                                uint256 existingStartIndex = claimCondition.currentStartId;
                                uint256 existingPhaseCount = claimCondition.count;
                                /**
                                 *  The mapping `supplyClaimedByWallet` uses a claim condition's UID as a key.
                                 *
                                 *  If `_resetClaimEligibility == true`, we assign completely new UIDs to the claim
                                 *  conditions in `_conditions`, effectively resetting the restrictions on claims expressed
                                 *  by `supplyClaimedByWallet`.
                                 */
                                uint256 newStartIndex = existingStartIndex;
                                if (_resetClaimEligibility) {
                                    newStartIndex = existingStartIndex + existingPhaseCount;
                                }
                                claimCondition.count = _conditions.length;
                                claimCondition.currentStartId = newStartIndex;
                                uint256 lastConditionStartTimestamp;
                                for (uint256 i = 0; i < _conditions.length; i++) {
                                    require(i == 0 || lastConditionStartTimestamp < _conditions[i].startTimestamp, "ST");
                                    uint256 supplyClaimedAlready = claimCondition.conditions[newStartIndex + i].supplyClaimed;
                                    if (supplyClaimedAlready > _conditions[i].maxClaimableSupply) {
                                        revert DropExceedMaxSupply();
                                    }
                                    claimCondition.conditions[newStartIndex + i] = _conditions[i];
                                    claimCondition.conditions[newStartIndex + i].supplyClaimed = supplyClaimedAlready;
                                    lastConditionStartTimestamp = _conditions[i].startTimestamp;
                                }
                                /**
                                 *  Gas refunds (as much as possible)
                                 *
                                 *  If `_resetClaimEligibility == true`, we assign completely new UIDs to the claim
                                 *  conditions in `_conditions`. So, we delete claim conditions with UID < `newStartIndex`.
                                 *
                                 *  If `_resetClaimEligibility == false`, and there are more existing claim conditions
                                 *  than in `_conditions`, we delete the existing claim conditions that don't get replaced
                                 *  by the conditions in `_conditions`.
                                 */
                                if (_resetClaimEligibility) {
                                    for (uint256 i = existingStartIndex; i < newStartIndex; i++) {
                                        delete claimCondition.conditions[i];
                                    }
                                } else {
                                    if (existingPhaseCount > _conditions.length) {
                                        for (uint256 i = _conditions.length; i < existingPhaseCount; i++) {
                                            delete claimCondition.conditions[newStartIndex + i];
                                        }
                                    }
                                }
                                emit ClaimConditionsUpdated(_conditions, _resetClaimEligibility);
                            }
                            /// @dev Checks a request to claim NFTs against the active claim condition's criteria.
                            function verifyClaim(
                                uint256 _conditionId,
                                address _claimer,
                                uint256 _quantity,
                                address _currency,
                                uint256 _pricePerToken,
                                AllowlistProof calldata _allowlistProof
                            ) public view virtual returns (bool isOverride) {
                                ClaimCondition memory currentClaimPhase = claimCondition.conditions[_conditionId];
                                uint256 claimLimit = currentClaimPhase.quantityLimitPerWallet;
                                uint256 claimPrice = currentClaimPhase.pricePerToken;
                                address claimCurrency = currentClaimPhase.currency;
                                /*
                                 * Here `isOverride` implies that if the merkle proof verification fails,
                                 * the claimer would claim through open claim limit instead of allowlisted limit.
                                 */
                                if (currentClaimPhase.merkleRoot != bytes32(0)) {
                                    (isOverride, ) = MerkleProof.verify(
                                        _allowlistProof.proof,
                                        currentClaimPhase.merkleRoot,
                                        keccak256(
                                            abi.encodePacked(
                                                _claimer,
                                                _allowlistProof.quantityLimitPerWallet,
                                                _allowlistProof.pricePerToken,
                                                _allowlistProof.currency
                                            )
                                        )
                                    );
                                }
                                if (isOverride) {
                                    claimLimit = _allowlistProof.quantityLimitPerWallet != 0
                                        ? _allowlistProof.quantityLimitPerWallet
                                        : claimLimit;
                                    claimPrice = _allowlistProof.pricePerToken != type(uint256).max
                                        ? _allowlistProof.pricePerToken
                                        : claimPrice;
                                    claimCurrency = _allowlistProof.pricePerToken != type(uint256).max && _allowlistProof.currency != address(0)
                                        ? _allowlistProof.currency
                                        : claimCurrency;
                                }
                                uint256 supplyClaimedByWallet = claimCondition.supplyClaimedByWallet[_conditionId][_claimer];
                                if (_currency != claimCurrency || _pricePerToken != claimPrice) {
                                    revert DropClaimInvalidTokenPrice(_currency, _pricePerToken, claimCurrency, claimPrice);
                                }
                                if (_quantity == 0 || (_quantity + supplyClaimedByWallet > claimLimit)) {
                                    revert DropClaimExceedLimit(claimLimit, _quantity + supplyClaimedByWallet);
                                }
                                if (currentClaimPhase.supplyClaimed + _quantity > currentClaimPhase.maxClaimableSupply) {
                                    revert DropClaimExceedMaxSupply(
                                        currentClaimPhase.maxClaimableSupply,
                                        currentClaimPhase.supplyClaimed + _quantity
                                    );
                                }
                                if (currentClaimPhase.startTimestamp > block.timestamp) {
                                    revert DropClaimNotStarted(currentClaimPhase.startTimestamp, block.timestamp);
                                }
                            }
                            /// @dev At any given moment, returns the uid for the active claim condition.
                            function getActiveClaimConditionId() public view returns (uint256) {
                                for (uint256 i = claimCondition.currentStartId + claimCondition.count; i > claimCondition.currentStartId; i--) {
                                    if (block.timestamp >= claimCondition.conditions[i - 1].startTimestamp) {
                                        return i - 1;
                                    }
                                }
                                revert DropNoActiveCondition();
                            }
                            /// @dev Returns the claim condition at the given uid.
                            function getClaimConditionById(uint256 _conditionId) external view returns (ClaimCondition memory condition) {
                                condition = claimCondition.conditions[_conditionId];
                            }
                            /// @dev Returns the supply claimed by claimer for a given conditionId.
                            function getSupplyClaimedByWallet(
                                uint256 _conditionId,
                                address _claimer
                            ) public view returns (uint256 supplyClaimedByWallet) {
                                supplyClaimedByWallet = claimCondition.supplyClaimedByWallet[_conditionId][_claimer];
                            }
                            /*////////////////////////////////////////////////////////////////////
                                Optional hooks that can be implemented in the derived contract
                            ///////////////////////////////////////////////////////////////////*/
                            /// @dev Exposes the ability to override the msg sender.
                            function _dropMsgSender() internal virtual returns (address) {
                                return msg.sender;
                            }
                            /// @dev Runs before every `claim` function call.
                            function _beforeClaim(
                                address _receiver,
                                uint256 _quantity,
                                address _currency,
                                uint256 _pricePerToken,
                                AllowlistProof calldata _allowlistProof,
                                bytes memory _data
                            ) internal virtual {}
                            /// @dev Runs after every `claim` function call.
                            function _afterClaim(
                                address _receiver,
                                uint256 _quantity,
                                address _currency,
                                uint256 _pricePerToken,
                                AllowlistProof calldata _allowlistProof,
                                bytes memory _data
                            ) internal virtual {}
                            /*///////////////////////////////////////////////////////////////
                                Virtual functions: to be implemented in derived contract
                            //////////////////////////////////////////////////////////////*/
                            /// @dev Collects and distributes the primary sale value of NFTs being claimed.
                            function _collectPriceOnClaim(
                                address _primarySaleRecipient,
                                uint256 _quantityToClaim,
                                address _currency,
                                uint256 _pricePerToken
                            ) internal virtual;
                            /// @dev Transfers the NFTs being claimed.
                            function _transferTokensOnClaim(
                                address _to,
                                uint256 _quantityBeingClaimed
                            ) internal virtual returns (uint256 startTokenId);
                            /// @dev Determine what wallet can update claim conditions
                            function _canSetClaimConditions() internal view virtual returns (bool);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./interface/ILazyMint.sol";
                        import "./BatchMintMetadata.sol";
                        /**
                         *  The `LazyMint` is a contract extension for any base NFT contract. It lets you 'lazy mint' any number of NFTs
                         *  at once. Here, 'lazy mint' means defining the metadata for particular tokenIds of your NFT contract, without actually
                         *  minting a non-zero balance of NFTs of those tokenIds.
                         */
                        abstract contract LazyMint is ILazyMint, BatchMintMetadata {
                            /// @dev The sender is not authorized to perform the action
                            error LazyMintUnauthorized();
                            error LazyMintInvalidAmount();
                            /// @notice The tokenId assigned to the next new NFT to be lazy minted.
                            uint256 internal nextTokenIdToLazyMint;
                            /**
                             *  @notice                  Lets an authorized address lazy mint a given amount of NFTs.
                             *
                             *  @param _amount           The number of NFTs to lazy mint.
                             *  @param _baseURIForTokens The base URI for the 'n' number of NFTs being lazy minted, where the metadata for each
                             *                           of those NFTs is `${baseURIForTokens}/${tokenId}`.
                             *  @param _data             Additional bytes data to be used at the discretion of the consumer of the contract.
                             *  @return batchId          A unique integer identifier for the batch of NFTs lazy minted together.
                             */
                            function lazyMint(
                                uint256 _amount,
                                string calldata _baseURIForTokens,
                                bytes calldata _data
                            ) public virtual override returns (uint256 batchId) {
                                if (!_canLazyMint()) {
                                    revert LazyMintUnauthorized();
                                }
                                if (_amount == 0) {
                                    revert LazyMintInvalidAmount();
                                }
                                uint256 startId = nextTokenIdToLazyMint;
                                (nextTokenIdToLazyMint, batchId) = _batchMintMetadata(startId, _amount, _baseURIForTokens);
                                emit TokensLazyMinted(startId, startId + _amount - 1, _baseURIForTokens, _data);
                                return batchId;
                            }
                            /// @dev Returns whether lazy minting can be performed in the given execution context.
                            function _canLazyMint() internal view virtual returns (bool);
                        }
                        // SPDX-License-Identifier: Apache 2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "../lib/Address.sol";
                        import "./interface/IMulticall.sol";
                        /**
                         * @dev Provides a function to batch together multiple calls in a single external call.
                         *
                         * _Available since v4.1._
                         */
                        contract Multicall is IMulticall {
                            /**
                             *  @notice Receives and executes a batch of function calls on this contract.
                             *  @dev Receives and executes a batch of function calls on this contract.
                             *
                             *  @param data The bytes data that makes up the batch of function calls to execute.
                             *  @return results The bytes data that makes up the result of the batch of function calls executed.
                             */
                            function multicall(bytes[] calldata data) external returns (bytes[] memory results) {
                                results = new bytes[](data.length);
                                address sender = _msgSender();
                                bool isForwarder = msg.sender != sender;
                                for (uint256 i = 0; i < data.length; i++) {
                                    if (isForwarder) {
                                        results[i] = Address.functionDelegateCall(address(this), abi.encodePacked(data[i], sender));
                                    } else {
                                        results[i] = Address.functionDelegateCall(address(this), data[i]);
                                    }
                                }
                                return results;
                            }
                            /// @notice Returns the sender in the given execution context.
                            function _msgSender() internal view virtual returns (address) {
                                return msg.sender;
                            }
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./interface/IOwnable.sol";
                        /**
                         *  @title   Ownable
                         *  @notice  Thirdweb's `Ownable` is a contract extension to be used with any base contract. It exposes functions for setting and reading
                         *           who the 'owner' of the inheriting smart contract is, and lets the inheriting contract perform conditional logic that uses
                         *           information about who the contract's owner is.
                         */
                        abstract contract Ownable is IOwnable {
                            /// @dev The sender is not authorized to perform the action
                            error OwnableUnauthorized();
                            /// @dev Owner of the contract (purpose: OpenSea compatibility)
                            address private _owner;
                            /// @dev Reverts if caller is not the owner.
                            modifier onlyOwner() {
                                if (msg.sender != _owner) {
                                    revert OwnableUnauthorized();
                                }
                                _;
                            }
                            /**
                             *  @notice Returns the owner of the contract.
                             */
                            function owner() public view override returns (address) {
                                return _owner;
                            }
                            /**
                             *  @notice Lets an authorized wallet set a new owner for the contract.
                             *  @param _newOwner The address to set as the new owner of the contract.
                             */
                            function setOwner(address _newOwner) external override {
                                if (!_canSetOwner()) {
                                    revert OwnableUnauthorized();
                                }
                                _setupOwner(_newOwner);
                            }
                            /// @dev Lets a contract admin set a new owner for the contract. The new owner must be a contract admin.
                            function _setupOwner(address _newOwner) internal {
                                address _prevOwner = _owner;
                                _owner = _newOwner;
                                emit OwnerUpdated(_prevOwner, _newOwner);
                            }
                            /// @dev Returns whether owner can be set in the given execution context.
                            function _canSetOwner() internal view virtual returns (bool);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./interface/IPermissions.sol";
                        import "../lib/Strings.sol";
                        /**
                         *  @title   Permissions
                         *  @dev     This contracts provides extending-contracts with role-based access control mechanisms
                         */
                        contract Permissions is IPermissions {
                            /// @dev The `account` is missing a role.
                            error PermissionsUnauthorizedAccount(address account, bytes32 neededRole);
                            /// @dev The `account` already is a holder of `role`
                            error PermissionsAlreadyGranted(address account, bytes32 role);
                            /// @dev Invalid priviledge to revoke
                            error PermissionsInvalidPermission(address expected, address actual);
                            /// @dev Map from keccak256 hash of a role => a map from address => whether address has role.
                            mapping(bytes32 => mapping(address => bool)) private _hasRole;
                            /// @dev Map from keccak256 hash of a role to role admin. See {getRoleAdmin}.
                            mapping(bytes32 => bytes32) private _getRoleAdmin;
                            /// @dev Default admin role for all roles. Only accounts with this role can grant/revoke other roles.
                            bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
                            /// @dev Modifier that checks if an account has the specified role; reverts otherwise.
                            modifier onlyRole(bytes32 role) {
                                _checkRole(role, msg.sender);
                                _;
                            }
                            /**
                             *  @notice         Checks whether an account has a particular role.
                             *  @dev            Returns `true` if `account` has been granted `role`.
                             *
                             *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                             *  @param account  Address of the account for which the role is being checked.
                             */
                            function hasRole(bytes32 role, address account) public view override returns (bool) {
                                return _hasRole[role][account];
                            }
                            /**
                             *  @notice         Checks whether an account has a particular role;
                             *                  role restrictions can be swtiched on and off.
                             *
                             *  @dev            Returns `true` if `account` has been granted `role`.
                             *                  Role restrictions can be swtiched on and off:
                             *                      - If address(0) has ROLE, then the ROLE restrictions
                             *                        don't apply.
                             *                      - If address(0) does not have ROLE, then the ROLE
                             *                        restrictions will apply.
                             *
                             *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                             *  @param account  Address of the account for which the role is being checked.
                             */
                            function hasRoleWithSwitch(bytes32 role, address account) public view returns (bool) {
                                if (!_hasRole[role][address(0)]) {
                                    return _hasRole[role][account];
                                }
                                return true;
                            }
                            /**
                             *  @notice         Returns the admin role that controls the specified role.
                             *  @dev            See {grantRole} and {revokeRole}.
                             *                  To change a role's admin, use {_setRoleAdmin}.
                             *
                             *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                             */
                            function getRoleAdmin(bytes32 role) external view override returns (bytes32) {
                                return _getRoleAdmin[role];
                            }
                            /**
                             *  @notice         Grants a role to an account, if not previously granted.
                             *  @dev            Caller must have admin role for the `role`.
                             *                  Emits {RoleGranted Event}.
                             *
                             *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                             *  @param account  Address of the account to which the role is being granted.
                             */
                            function grantRole(bytes32 role, address account) public virtual override {
                                _checkRole(_getRoleAdmin[role], msg.sender);
                                if (_hasRole[role][account]) {
                                    revert PermissionsAlreadyGranted(account, role);
                                }
                                _setupRole(role, account);
                            }
                            /**
                             *  @notice         Revokes role from an account.
                             *  @dev            Caller must have admin role for the `role`.
                             *                  Emits {RoleRevoked Event}.
                             *
                             *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                             *  @param account  Address of the account from which the role is being revoked.
                             */
                            function revokeRole(bytes32 role, address account) public virtual override {
                                _checkRole(_getRoleAdmin[role], msg.sender);
                                _revokeRole(role, account);
                            }
                            /**
                             *  @notice         Revokes role from the account.
                             *  @dev            Caller must have the `role`, with caller being the same as `account`.
                             *                  Emits {RoleRevoked Event}.
                             *
                             *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                             *  @param account  Address of the account from which the role is being revoked.
                             */
                            function renounceRole(bytes32 role, address account) public virtual override {
                                if (msg.sender != account) {
                                    revert PermissionsInvalidPermission(msg.sender, account);
                                }
                                _revokeRole(role, account);
                            }
                            /// @dev Sets `adminRole` as `role`'s admin role.
                            function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                                bytes32 previousAdminRole = _getRoleAdmin[role];
                                _getRoleAdmin[role] = adminRole;
                                emit RoleAdminChanged(role, previousAdminRole, adminRole);
                            }
                            /// @dev Sets up `role` for `account`
                            function _setupRole(bytes32 role, address account) internal virtual {
                                _hasRole[role][account] = true;
                                emit RoleGranted(role, account, msg.sender);
                            }
                            /// @dev Revokes `role` from `account`
                            function _revokeRole(bytes32 role, address account) internal virtual {
                                _checkRole(role, account);
                                delete _hasRole[role][account];
                                emit RoleRevoked(role, account, msg.sender);
                            }
                            /// @dev Checks `role` for `account`. Reverts with a message including the required role.
                            function _checkRole(bytes32 role, address account) internal view virtual {
                                if (!_hasRole[role][account]) {
                                    revert PermissionsUnauthorizedAccount(account, role);
                                }
                            }
                            /// @dev Checks `role` for `account`. Reverts with a message including the required role.
                            function _checkRoleWithSwitch(bytes32 role, address account) internal view virtual {
                                if (!hasRoleWithSwitch(role, account)) {
                                    revert PermissionsUnauthorizedAccount(account, role);
                                }
                            }
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./interface/IPermissionsEnumerable.sol";
                        import "./Permissions.sol";
                        /**
                         *  @title   PermissionsEnumerable
                         *  @dev     This contracts provides extending-contracts with role-based access control mechanisms.
                         *           Also provides interfaces to view all members with a given role, and total count of members.
                         */
                        contract PermissionsEnumerable is IPermissionsEnumerable, Permissions {
                            /**
                             *  @notice A data structure to store data of members for a given role.
                             *
                             *  @param index    Current index in the list of accounts that have a role.
                             *  @param members  map from index => address of account that has a role
                             *  @param indexOf  map from address => index which the account has.
                             */
                            struct RoleMembers {
                                uint256 index;
                                mapping(uint256 => address) members;
                                mapping(address => uint256) indexOf;
                            }
                            /// @dev map from keccak256 hash of a role to its members' data. See {RoleMembers}.
                            mapping(bytes32 => RoleMembers) private roleMembers;
                            /**
                             *  @notice         Returns the role-member from a list of members for a role,
                             *                  at a given index.
                             *  @dev            Returns `member` who has `role`, at `index` of role-members list.
                             *                  See struct {RoleMembers}, and mapping {roleMembers}
                             *
                             *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                             *  @param index    Index in list of current members for the role.
                             *
                             *  @return member  Address of account that has `role`
                             */
                            function getRoleMember(bytes32 role, uint256 index) external view override returns (address member) {
                                uint256 currentIndex = roleMembers[role].index;
                                uint256 check;
                                for (uint256 i = 0; i < currentIndex; i += 1) {
                                    if (roleMembers[role].members[i] != address(0)) {
                                        if (check == index) {
                                            member = roleMembers[role].members[i];
                                            return member;
                                        }
                                        check += 1;
                                    } else if (hasRole(role, address(0)) && i == roleMembers[role].indexOf[address(0)]) {
                                        check += 1;
                                    }
                                }
                            }
                            /**
                             *  @notice         Returns total number of accounts that have a role.
                             *  @dev            Returns `count` of accounts that have `role`.
                             *                  See struct {RoleMembers}, and mapping {roleMembers}
                             *
                             *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                             *
                             *  @return count   Total number of accounts that have `role`
                             */
                            function getRoleMemberCount(bytes32 role) external view override returns (uint256 count) {
                                uint256 currentIndex = roleMembers[role].index;
                                for (uint256 i = 0; i < currentIndex; i += 1) {
                                    if (roleMembers[role].members[i] != address(0)) {
                                        count += 1;
                                    }
                                }
                                if (hasRole(role, address(0))) {
                                    count += 1;
                                }
                            }
                            /// @dev Revokes `role` from `account`, and removes `account` from {roleMembers}
                            ///      See {_removeMember}
                            function _revokeRole(bytes32 role, address account) internal override {
                                super._revokeRole(role, account);
                                _removeMember(role, account);
                            }
                            /// @dev Grants `role` to `account`, and adds `account` to {roleMembers}
                            ///      See {_addMember}
                            function _setupRole(bytes32 role, address account) internal override {
                                super._setupRole(role, account);
                                _addMember(role, account);
                            }
                            /// @dev adds `account` to {roleMembers}, for `role`
                            function _addMember(bytes32 role, address account) internal {
                                uint256 idx = roleMembers[role].index;
                                roleMembers[role].index += 1;
                                roleMembers[role].members[idx] = account;
                                roleMembers[role].indexOf[account] = idx;
                            }
                            /// @dev removes `account` from {roleMembers}, for `role`
                            function _removeMember(bytes32 role, address account) internal {
                                uint256 idx = roleMembers[role].indexOf[account];
                                delete roleMembers[role].members[idx];
                                delete roleMembers[role].indexOf[account];
                            }
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./interface/IPlatformFee.sol";
                        /**
                         *  @title   Platform Fee
                         *  @notice  Thirdweb's `PlatformFee` is a contract extension to be used with any base contract. It exposes functions for setting and reading
                         *           the recipient of platform fee and the platform fee basis points, and lets the inheriting contract perform conditional logic
                         *           that uses information about platform fees, if desired.
                         */
                        abstract contract PlatformFee is IPlatformFee {
                            /// @dev The sender is not authorized to perform the action
                            error PlatformFeeUnauthorized();
                            /// @dev The recipient is invalid
                            error PlatformFeeInvalidRecipient(address recipient);
                            /// @dev The fee bps exceeded the max value
                            error PlatformFeeExceededMaxFeeBps(uint256 max, uint256 actual);
                            /// @dev The address that receives all platform fees from all sales.
                            address private platformFeeRecipient;
                            /// @dev The % of primary sales collected as platform fees.
                            uint16 private platformFeeBps;
                            /// @dev Fee type variants: percentage fee and flat fee
                            PlatformFeeType private platformFeeType;
                            /// @dev The flat amount collected by the contract as fees on primary sales.
                            uint256 private flatPlatformFee;
                            /// @dev Returns the platform fee recipient and bps.
                            function getPlatformFeeInfo() public view override returns (address, uint16) {
                                return (platformFeeRecipient, uint16(platformFeeBps));
                            }
                            /// @dev Returns the platform fee bps and recipient.
                            function getFlatPlatformFeeInfo() public view returns (address, uint256) {
                                return (platformFeeRecipient, flatPlatformFee);
                            }
                            /// @dev Returns the platform fee type.
                            function getPlatformFeeType() public view returns (PlatformFeeType) {
                                return platformFeeType;
                            }
                            /**
                             *  @notice         Updates the platform fee recipient and bps.
                             *  @dev            Caller should be authorized to set platform fee info.
                             *                  See {_canSetPlatformFeeInfo}.
                             *                  Emits {PlatformFeeInfoUpdated Event}; See {_setupPlatformFeeInfo}.
                             *
                             *  @param _platformFeeRecipient   Address to be set as new platformFeeRecipient.
                             *  @param _platformFeeBps         Updated platformFeeBps.
                             */
                            function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external override {
                                if (!_canSetPlatformFeeInfo()) {
                                    revert PlatformFeeUnauthorized();
                                }
                                _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps);
                            }
                            /// @dev Sets the platform fee recipient and bps
                            function _setupPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) internal {
                                if (_platformFeeBps > 10_000) {
                                    revert PlatformFeeExceededMaxFeeBps(10_000, _platformFeeBps);
                                }
                                if (_platformFeeRecipient == address(0)) {
                                    revert PlatformFeeInvalidRecipient(_platformFeeRecipient);
                                }
                                platformFeeBps = uint16(_platformFeeBps);
                                platformFeeRecipient = _platformFeeRecipient;
                                emit PlatformFeeInfoUpdated(_platformFeeRecipient, _platformFeeBps);
                            }
                            /// @notice Lets a module admin set a flat fee on primary sales.
                            function setFlatPlatformFeeInfo(address _platformFeeRecipient, uint256 _flatFee) external {
                                if (!_canSetPlatformFeeInfo()) {
                                    revert PlatformFeeUnauthorized();
                                }
                                _setupFlatPlatformFeeInfo(_platformFeeRecipient, _flatFee);
                            }
                            /// @dev Sets a flat fee on primary sales.
                            function _setupFlatPlatformFeeInfo(address _platformFeeRecipient, uint256 _flatFee) internal {
                                flatPlatformFee = _flatFee;
                                platformFeeRecipient = _platformFeeRecipient;
                                emit FlatPlatformFeeUpdated(_platformFeeRecipient, _flatFee);
                            }
                            /// @notice Lets a module admin set platform fee type.
                            function setPlatformFeeType(PlatformFeeType _feeType) external {
                                if (!_canSetPlatformFeeInfo()) {
                                    revert PlatformFeeUnauthorized();
                                }
                                _setupPlatformFeeType(_feeType);
                            }
                            /// @dev Sets platform fee type.
                            function _setupPlatformFeeType(PlatformFeeType _feeType) internal {
                                platformFeeType = _feeType;
                                emit PlatformFeeTypeUpdated(_feeType);
                            }
                            /// @dev Returns whether platform fee info can be set in the given execution context.
                            function _canSetPlatformFeeInfo() internal view virtual returns (bool);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./interface/IPrimarySale.sol";
                        /**
                         *  @title   Primary Sale
                         *  @notice  Thirdweb's `PrimarySale` is a contract extension to be used with any base contract. It exposes functions for setting and reading
                         *           the recipient of primary sales, and lets the inheriting contract perform conditional logic that uses information about
                         *           primary sales, if desired.
                         */
                        abstract contract PrimarySale is IPrimarySale {
                            /// @dev The sender is not authorized to perform the action
                            error PrimarySaleUnauthorized();
                            /// @dev The recipient is invalid
                            error PrimarySaleInvalidRecipient(address recipient);
                            /// @dev The address that receives all primary sales value.
                            address private recipient;
                            /// @dev Returns primary sale recipient address.
                            function primarySaleRecipient() public view override returns (address) {
                                return recipient;
                            }
                            /**
                             *  @notice         Updates primary sale recipient.
                             *  @dev            Caller should be authorized to set primary sales info.
                             *                  See {_canSetPrimarySaleRecipient}.
                             *                  Emits {PrimarySaleRecipientUpdated Event}; See {_setupPrimarySaleRecipient}.
                             *
                             *  @param _saleRecipient   Address to be set as new recipient of primary sales.
                             */
                            function setPrimarySaleRecipient(address _saleRecipient) external override {
                                if (!_canSetPrimarySaleRecipient()) {
                                    revert PrimarySaleUnauthorized();
                                }
                                _setupPrimarySaleRecipient(_saleRecipient);
                            }
                            /// @dev Lets a contract admin set the recipient for all primary sales.
                            function _setupPrimarySaleRecipient(address _saleRecipient) internal {
                                if (_saleRecipient == address(0)) {
                                    revert PrimarySaleInvalidRecipient(_saleRecipient);
                                }
                                recipient = _saleRecipient;
                                emit PrimarySaleRecipientUpdated(_saleRecipient);
                            }
                            /// @dev Returns whether primary sale recipient can be set in the given execution context.
                            function _canSetPrimarySaleRecipient() internal view virtual returns (bool);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./interface/IRoyalty.sol";
                        /**
                         *  @title   Royalty
                         *  @notice  Thirdweb's `Royalty` is a contract extension to be used with any base contract. It exposes functions for setting and reading
                         *           the recipient of royalty fee and the royalty fee basis points, and lets the inheriting contract perform conditional logic
                         *           that uses information about royalty fees, if desired.
                         *
                         *  @dev     The `Royalty` contract is ERC2981 compliant.
                         */
                        abstract contract Royalty is IRoyalty {
                            /// @dev The sender is not authorized to perform the action
                            error RoyaltyUnauthorized();
                            /// @dev The recipient is invalid
                            error RoyaltyInvalidRecipient(address recipient);
                            /// @dev The fee bps exceeded the max value
                            error RoyaltyExceededMaxFeeBps(uint256 max, uint256 actual);
                            /// @dev The (default) address that receives all royalty value.
                            address private royaltyRecipient;
                            /// @dev The (default) % of a sale to take as royalty (in basis points).
                            uint16 private royaltyBps;
                            /// @dev Token ID => royalty recipient and bps for token
                            mapping(uint256 => RoyaltyInfo) private royaltyInfoForToken;
                            /**
                             *  @notice   View royalty info for a given token and sale price.
                             *  @dev      Returns royalty amount and recipient for `tokenId` and `salePrice`.
                             *  @param tokenId          The tokenID of the NFT for which to query royalty info.
                             *  @param salePrice        Sale price of the token.
                             *
                             *  @return receiver        Address of royalty recipient account.
                             *  @return royaltyAmount   Royalty amount calculated at current royaltyBps value.
                             */
                            function royaltyInfo(
                                uint256 tokenId,
                                uint256 salePrice
                            ) external view virtual override returns (address receiver, uint256 royaltyAmount) {
                                (address recipient, uint256 bps) = getRoyaltyInfoForToken(tokenId);
                                receiver = recipient;
                                royaltyAmount = (salePrice * bps) / 10_000;
                            }
                            /**
                             *  @notice          View royalty info for a given token.
                             *  @dev             Returns royalty recipient and bps for `_tokenId`.
                             *  @param _tokenId  The tokenID of the NFT for which to query royalty info.
                             */
                            function getRoyaltyInfoForToken(uint256 _tokenId) public view override returns (address, uint16) {
                                RoyaltyInfo memory royaltyForToken = royaltyInfoForToken[_tokenId];
                                return
                                    royaltyForToken.recipient == address(0)
                                        ? (royaltyRecipient, uint16(royaltyBps))
                                        : (royaltyForToken.recipient, uint16(royaltyForToken.bps));
                            }
                            /**
                             *  @notice Returns the defualt royalty recipient and BPS for this contract's NFTs.
                             */
                            function getDefaultRoyaltyInfo() external view override returns (address, uint16) {
                                return (royaltyRecipient, uint16(royaltyBps));
                            }
                            /**
                             *  @notice         Updates default royalty recipient and bps.
                             *  @dev            Caller should be authorized to set royalty info.
                             *                  See {_canSetRoyaltyInfo}.
                             *                  Emits {DefaultRoyalty Event}; See {_setupDefaultRoyaltyInfo}.
                             *
                             *  @param _royaltyRecipient   Address to be set as default royalty recipient.
                             *  @param _royaltyBps         Updated royalty bps.
                             */
                            function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) external override {
                                if (!_canSetRoyaltyInfo()) {
                                    revert RoyaltyUnauthorized();
                                }
                                _setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps);
                            }
                            /// @dev Lets a contract admin update the default royalty recipient and bps.
                            function _setupDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) internal {
                                if (_royaltyBps > 10_000) {
                                    revert RoyaltyExceededMaxFeeBps(10_000, _royaltyBps);
                                }
                                royaltyRecipient = _royaltyRecipient;
                                royaltyBps = uint16(_royaltyBps);
                                emit DefaultRoyalty(_royaltyRecipient, _royaltyBps);
                            }
                            /**
                             *  @notice         Updates default royalty recipient and bps for a particular token.
                             *  @dev            Sets royalty info for `_tokenId`. Caller should be authorized to set royalty info.
                             *                  See {_canSetRoyaltyInfo}.
                             *                  Emits {RoyaltyForToken Event}; See {_setupRoyaltyInfoForToken}.
                             *
                             *  @param _recipient   Address to be set as royalty recipient for given token Id.
                             *  @param _bps         Updated royalty bps for the token Id.
                             */
                            function setRoyaltyInfoForToken(uint256 _tokenId, address _recipient, uint256 _bps) external override {
                                if (!_canSetRoyaltyInfo()) {
                                    revert RoyaltyUnauthorized();
                                }
                                _setupRoyaltyInfoForToken(_tokenId, _recipient, _bps);
                            }
                            /// @dev Lets a contract admin set the royalty recipient and bps for a particular token Id.
                            function _setupRoyaltyInfoForToken(uint256 _tokenId, address _recipient, uint256 _bps) internal {
                                if (_bps > 10_000) {
                                    revert RoyaltyExceededMaxFeeBps(10_000, _bps);
                                }
                                royaltyInfoForToken[_tokenId] = RoyaltyInfo({ recipient: _recipient, bps: _bps });
                                emit RoyaltyForToken(_tokenId, _recipient, _bps);
                            }
                            /// @dev Returns whether royalty info can be set in the given execution context.
                            function _canSetRoyaltyInfo() internal view virtual returns (bool);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        /**
                         *  The interface `IClaimCondition` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
                         *
                         *  A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
                         *  or added to by the contract admin. At any moment, there is only one active claim condition.
                         */
                        interface IClaimCondition {
                            /**
                             *  @notice The criteria that make up a claim condition.
                             *
                             *  @param startTimestamp                 The unix timestamp after which the claim condition applies.
                             *                                        The same claim condition applies until the `startTimestamp`
                             *                                        of the next claim condition.
                             *
                             *  @param maxClaimableSupply             The maximum total number of tokens that can be claimed under
                             *                                        the claim condition.
                             *
                             *  @param supplyClaimed                  At any given point, the number of tokens that have been claimed
                             *                                        under the claim condition.
                             *
                             *  @param quantityLimitPerWallet         The maximum number of tokens that can be claimed by a wallet.
                             *
                             *  @param merkleRoot                     The allowlist of addresses that can claim tokens under the claim
                             *                                        condition.
                             *
                             *  @param pricePerToken                  The price required to pay per token claimed.
                             *
                             *  @param currency                       The currency in which the `pricePerToken` must be paid.
                             *
                             *  @param metadata                       Claim condition metadata.
                             */
                            struct ClaimCondition {
                                uint256 startTimestamp;
                                uint256 maxClaimableSupply;
                                uint256 supplyClaimed;
                                uint256 quantityLimitPerWallet;
                                bytes32 merkleRoot;
                                uint256 pricePerToken;
                                address currency;
                                string metadata;
                            }
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./IClaimCondition.sol";
                        /**
                         *  The interface `IClaimConditionMultiPhase` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
                         *
                         *  An authorized wallet can set a series of claim conditions, ordered by their respective `startTimestamp`.
                         *  A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
                         *  or added to by the contract admin. At any moment, there is only one active claim condition.
                         */
                        interface IClaimConditionMultiPhase is IClaimCondition {
                            /**
                             *  @notice The set of all claim conditions, at any given moment.
                             *  Claim Phase ID = [currentStartId, currentStartId + length - 1];
                             *
                             *  @param currentStartId           The uid for the first claim condition amongst the current set of
                             *                                  claim conditions. The uid for each next claim condition is one
                             *                                  more than the previous claim condition's uid.
                             *
                             *  @param count                    The total number of phases / claim conditions in the list
                             *                                  of claim conditions.
                             *
                             *  @param conditions                   The claim conditions at a given uid. Claim conditions
                             *                                  are ordered in an ascending order by their `startTimestamp`.
                             *
                             *  @param supplyClaimedByWallet    Map from a claim condition uid and account to supply claimed by account.
                             */
                            struct ClaimConditionList {
                                uint256 currentStartId;
                                uint256 count;
                                mapping(uint256 => ClaimCondition) conditions;
                                mapping(uint256 => mapping(address => uint256)) supplyClaimedByWallet;
                            }
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        /**
                         *  Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
                         *  for you contract.
                         *
                         *  Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
                         */
                        interface IContractMetadata {
                            /// @dev Returns the metadata URI of the contract.
                            function contractURI() external view returns (string memory);
                            /**
                             *  @dev Sets contract URI for the storefront-level metadata of the contract.
                             *       Only module admin can call this function.
                             */
                            function setContractURI(string calldata _uri) external;
                            /// @dev Emitted when the contract URI is updated.
                            event ContractURIUpdated(string prevURI, string newURI);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        /**
                         *  Thirdweb's `DelayedReveal` is a contract extension for base NFT contracts. It lets you create batches of
                         *  'delayed-reveal' NFTs. You can learn more about the usage of delayed reveal NFTs here - https://blog.thirdweb.com/delayed-reveal-nfts
                         */
                        interface IDelayedReveal {
                            /// @dev Emitted when tokens are revealed.
                            event TokenURIRevealed(uint256 indexed index, string revealedURI);
                            /**
                             *  @notice Reveals a batch of delayed reveal NFTs.
                             *
                             *  @param identifier The ID for the batch of delayed-reveal NFTs to reveal.
                             *
                             *  @param key        The key with which the base URI for the relevant batch of NFTs was encrypted.
                             */
                            function reveal(uint256 identifier, bytes calldata key) external returns (string memory revealedURI);
                            /**
                             *  @notice Performs XOR encryption/decryption.
                             *
                             *  @param data The data to encrypt. In the case of delayed-reveal NFTs, this is the "revealed" state
                             *              base URI of the relevant batch of NFTs.
                             *
                             *  @param key  The key with which to encrypt data
                             */
                            function encryptDecrypt(bytes memory data, bytes calldata key) external pure returns (bytes memory result);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./IClaimConditionMultiPhase.sol";
                        /**
                         *  The interface `IDrop` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
                         *
                         *  An authorized wallet can set a series of claim conditions, ordered by their respective `startTimestamp`.
                         *  A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
                         *  or added to by the contract admin. At any moment, there is only one active claim condition.
                         */
                        interface IDrop is IClaimConditionMultiPhase {
                            /**
                             *  @param proof Proof of concerned wallet's inclusion in an allowlist.
                             *  @param quantityLimitPerWallet The total quantity of tokens the allowlisted wallet is eligible to claim over time.
                             *  @param pricePerToken The price per token the allowlisted wallet must pay to claim tokens.
                             *  @param currency The currency in which the allowlisted wallet must pay the price for claiming tokens.
                             */
                            struct AllowlistProof {
                                bytes32[] proof;
                                uint256 quantityLimitPerWallet;
                                uint256 pricePerToken;
                                address currency;
                            }
                            /// @notice Emitted when tokens are claimed via `claim`.
                            event TokensClaimed(
                                uint256 indexed claimConditionIndex,
                                address indexed claimer,
                                address indexed receiver,
                                uint256 startTokenId,
                                uint256 quantityClaimed
                            );
                            /// @notice Emitted when the contract's claim conditions are updated.
                            event ClaimConditionsUpdated(ClaimCondition[] claimConditions, bool resetEligibility);
                            /**
                             *  @notice Lets an account claim a given quantity of NFTs.
                             *
                             *  @param receiver                       The receiver of the NFTs to claim.
                             *  @param quantity                       The quantity of NFTs to claim.
                             *  @param currency                       The currency in which to pay for the claim.
                             *  @param pricePerToken                  The price per token to pay for the claim.
                             *  @param allowlistProof                 The proof of the claimer's inclusion in the merkle root allowlist
                             *                                        of the claim conditions that apply.
                             *  @param data                           Arbitrary bytes data that can be leveraged in the implementation of this interface.
                             */
                            function claim(
                                address receiver,
                                uint256 quantity,
                                address currency,
                                uint256 pricePerToken,
                                AllowlistProof calldata allowlistProof,
                                bytes memory data
                            ) external payable;
                            /**
                             *  @notice Lets a contract admin (account with `DEFAULT_ADMIN_ROLE`) set claim conditions.
                             *
                             *  @param phases                   Claim conditions in ascending order by `startTimestamp`.
                             *
                             *  @param resetClaimEligibility    Whether to honor the restrictions applied to wallets who have claimed tokens in the current conditions,
                             *                                  in the new claim conditions being set.
                             *
                             */
                            function setClaimConditions(ClaimCondition[] calldata phases, bool resetClaimEligibility) external;
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        /**
                         *  Thirdweb's `LazyMint` is a contract extension for any base NFT contract. It lets you 'lazy mint' any number of NFTs
                         *  at once. Here, 'lazy mint' means defining the metadata for particular tokenIds of your NFT contract, without actually
                         *  minting a non-zero balance of NFTs of those tokenIds.
                         */
                        interface ILazyMint {
                            /// @dev Emitted when tokens are lazy minted.
                            event TokensLazyMinted(uint256 indexed startTokenId, uint256 endTokenId, string baseURI, bytes encryptedBaseURI);
                            /**
                             *  @notice Lazy mints a given amount of NFTs.
                             *
                             *  @param amount           The number of NFTs to lazy mint.
                             *
                             *  @param baseURIForTokens The base URI for the 'n' number of NFTs being lazy minted, where the metadata for each
                             *                          of those NFTs is `${baseURIForTokens}/${tokenId}`.
                             *
                             *  @param extraData        Additional bytes data to be used at the discretion of the consumer of the contract.
                             *
                             *  @return batchId         A unique integer identifier for the batch of NFTs lazy minted together.
                             */
                            function lazyMint(
                                uint256 amount,
                                string calldata baseURIForTokens,
                                bytes calldata extraData
                            ) external returns (uint256 batchId);
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        /**
                         * @dev Provides a function to batch together multiple calls in a single external call.
                         *
                         * _Available since v4.1._
                         */
                        interface IMulticall {
                            /**
                             * @dev Receives and executes a batch of function calls on this contract.
                             */
                            function multicall(bytes[] calldata data) external returns (bytes[] memory results);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        /**
                         *  Thirdweb's `Ownable` is a contract extension to be used with any base contract. It exposes functions for setting and reading
                         *  who the 'owner' of the inheriting smart contract is, and lets the inheriting contract perform conditional logic that uses
                         *  information about who the contract's owner is.
                         */
                        interface IOwnable {
                            /// @dev Returns the owner of the contract.
                            function owner() external view returns (address);
                            /// @dev Lets a module admin set a new owner for the contract. The new owner must be a module admin.
                            function setOwner(address _newOwner) external;
                            /// @dev Emitted when a new Owner is set.
                            event OwnerUpdated(address indexed prevOwner, address indexed newOwner);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        /**
                         * @dev External interface of AccessControl declared to support ERC165 detection.
                         */
                        interface IPermissions {
                            /**
                             * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
                             *
                             * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
                             * {RoleAdminChanged} not being emitted signaling this.
                             *
                             * _Available since v3.1._
                             */
                            event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
                            /**
                             * @dev Emitted when `account` is granted `role`.
                             *
                             * `sender` is the account that originated the contract call, an admin role
                             * bearer except when using {AccessControl-_setupRole}.
                             */
                            event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
                            /**
                             * @dev Emitted when `account` is revoked `role`.
                             *
                             * `sender` is the account that originated the contract call:
                             *   - if using `revokeRole`, it is the admin role bearer
                             *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
                             */
                            event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
                            /**
                             * @dev Returns `true` if `account` has been granted `role`.
                             */
                            function hasRole(bytes32 role, address account) external view returns (bool);
                            /**
                             * @dev Returns the admin role that controls `role`. See {grantRole} and
                             * {revokeRole}.
                             *
                             * To change a role's admin, use {AccessControl-_setRoleAdmin}.
                             */
                            function getRoleAdmin(bytes32 role) external view returns (bytes32);
                            /**
                             * @dev Grants `role` to `account`.
                             *
                             * If `account` had not been already granted `role`, emits a {RoleGranted}
                             * event.
                             *
                             * Requirements:
                             *
                             * - the caller must have ``role``'s admin role.
                             */
                            function grantRole(bytes32 role, address account) external;
                            /**
                             * @dev Revokes `role` from `account`.
                             *
                             * If `account` had been granted `role`, emits a {RoleRevoked} event.
                             *
                             * Requirements:
                             *
                             * - the caller must have ``role``'s admin role.
                             */
                            function revokeRole(bytes32 role, address account) external;
                            /**
                             * @dev Revokes `role` from the calling account.
                             *
                             * Roles are often managed via {grantRole} and {revokeRole}: this function's
                             * purpose is to provide a mechanism for accounts to lose their privileges
                             * if they are compromised (such as when a trusted device is misplaced).
                             *
                             * If the calling account had been granted `role`, emits a {RoleRevoked}
                             * event.
                             *
                             * Requirements:
                             *
                             * - the caller must be `account`.
                             */
                            function renounceRole(bytes32 role, address account) external;
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "./IPermissions.sol";
                        /**
                         * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
                         */
                        interface IPermissionsEnumerable is IPermissions {
                            /**
                             * @dev Returns one of the accounts that have `role`. `index` must be a
                             * value between 0 and {getRoleMemberCount}, non-inclusive.
                             *
                             * Role bearers are not sorted in any particular way, and their ordering may
                             * change at any point.
                             *
                             * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
                             * you perform all queries on the same block. See the following
                             * [forum post](https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296)
                             * for more information.
                             */
                            function getRoleMember(bytes32 role, uint256 index) external view returns (address);
                            /**
                             * @dev Returns the number of accounts that have `role`. Can be used
                             * together with {getRoleMember} to enumerate all bearers of a role.
                             */
                            function getRoleMemberCount(bytes32 role) external view returns (uint256);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        /**
                         *  Thirdweb's `PlatformFee` is a contract extension to be used with any base contract. It exposes functions for setting and reading
                         *  the recipient of platform fee and the platform fee basis points, and lets the inheriting contract perform conditional logic
                         *  that uses information about platform fees, if desired.
                         */
                        interface IPlatformFee {
                            /// @dev Fee type variants: percentage fee and flat fee
                            enum PlatformFeeType {
                                Bps,
                                Flat
                            }
                            /// @dev Returns the platform fee bps and recipient.
                            function getPlatformFeeInfo() external view returns (address, uint16);
                            /// @dev Lets a module admin update the fees on primary sales.
                            function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external;
                            /// @dev Emitted when fee on primary sales is updated.
                            event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps);
                            /// @dev Emitted when the flat platform fee is updated.
                            event FlatPlatformFeeUpdated(address platformFeeRecipient, uint256 flatFee);
                            /// @dev Emitted when the platform fee type is updated.
                            event PlatformFeeTypeUpdated(PlatformFeeType feeType);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        /**
                         *  Thirdweb's `Primary` is a contract extension to be used with any base contract. It exposes functions for setting and reading
                         *  the recipient of primary sales, and lets the inheriting contract perform conditional logic that uses information about
                         *  primary sales, if desired.
                         */
                        interface IPrimarySale {
                            /// @dev The adress that receives all primary sales value.
                            function primarySaleRecipient() external view returns (address);
                            /// @dev Lets a module admin set the default recipient of all primary sales.
                            function setPrimarySaleRecipient(address _saleRecipient) external;
                            /// @dev Emitted when a new sale recipient is set.
                            event PrimarySaleRecipientUpdated(address indexed recipient);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        import "../../eip/interface/IERC2981.sol";
                        /**
                         *  Thirdweb's `Royalty` is a contract extension to be used with any base contract. It exposes functions for setting and reading
                         *  the recipient of royalty fee and the royalty fee basis points, and lets the inheriting contract perform conditional logic
                         *  that uses information about royalty fees, if desired.
                         *
                         *  The `Royalty` contract is ERC2981 compliant.
                         */
                        interface IRoyalty is IERC2981 {
                            struct RoyaltyInfo {
                                address recipient;
                                uint256 bps;
                            }
                            /// @dev Returns the royalty recipient and fee bps.
                            function getDefaultRoyaltyInfo() external view returns (address, uint16);
                            /// @dev Lets a module admin update the royalty bps and recipient.
                            function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) external;
                            /// @dev Lets a module admin set the royalty recipient for a particular token Id.
                            function setRoyaltyInfoForToken(uint256 tokenId, address recipient, uint256 bps) external;
                            /// @dev Returns the royalty recipient for a particular token Id.
                            function getRoyaltyInfoForToken(uint256 tokenId) external view returns (address, uint16);
                            /// @dev Emitted when royalty info is updated.
                            event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps);
                            /// @dev Emitted when royalty recipient for tokenId is set
                            event RoyaltyForToken(uint256 indexed tokenId, address indexed royaltyRecipient, uint256 royaltyBps);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts v4.4.0 (metatx/ERC2771Context.sol)
                        pragma solidity ^0.8.11;
                        import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
                        import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
                        /**
                         * @dev Context variant with ERC2771 support.
                         */
                        abstract contract ERC2771ContextUpgradeable is Initializable, ContextUpgradeable {
                            mapping(address => bool) private _trustedForwarder;
                            function __ERC2771Context_init(address[] memory trustedForwarder) internal onlyInitializing {
                                __Context_init_unchained();
                                __ERC2771Context_init_unchained(trustedForwarder);
                            }
                            function __ERC2771Context_init_unchained(address[] memory trustedForwarder) internal onlyInitializing {
                                for (uint256 i = 0; i < trustedForwarder.length; i++) {
                                    _trustedForwarder[trustedForwarder[i]] = true;
                                }
                            }
                            function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
                                return _trustedForwarder[forwarder];
                            }
                            function _msgSender() internal view virtual override returns (address sender) {
                                if (isTrustedForwarder(msg.sender)) {
                                    // The assembly code is more direct than the Solidity version using `abi.decode`.
                                    assembly {
                                        sender := shr(96, calldataload(sub(calldatasize(), 20)))
                                    }
                                } else {
                                    return super._msgSender();
                                }
                            }
                            function _msgData() internal view virtual override returns (bytes calldata) {
                                if (isTrustedForwarder(msg.sender)) {
                                    return msg.data[:msg.data.length - 20];
                                } else {
                                    return super._msgData();
                                }
                            }
                            uint256[49] private __gap;
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)
                        pragma solidity ^0.8.0;
                        import "../../../../../eip/interface/IERC20.sol";
                        import { Address } from "../../../../../lib/Address.sol";
                        /**
                         * @title SafeERC20
                         * @dev Wrappers around ERC20 operations that throw on failure (when the token
                         * contract returns false). Tokens that return no value (and instead revert or
                         * throw on failure) are also supported, non-reverting calls are assumed to be
                         * successful.
                         * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
                         * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
                         */
                        library SafeERC20 {
                            using Address for address;
                            function safeTransfer(IERC20 token, address to, uint256 value) internal {
                                _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                            }
                            function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                                _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                            }
                            /**
                             * @dev Deprecated. This function has issues similar to the ones found in
                             * {IERC20-approve}, and its usage is discouraged.
                             *
                             * Whenever possible, use {safeIncreaseAllowance} and
                             * {safeDecreaseAllowance} instead.
                             */
                            function safeApprove(IERC20 token, address spender, uint256 value) internal {
                                // safeApprove should only be called when setting an initial allowance,
                                // or when resetting it to zero. To increase and decrease it, use
                                // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                                require(
                                    (value == 0) || (token.allowance(address(this), spender) == 0),
                                    "SafeERC20: approve from non-zero to non-zero allowance"
                                );
                                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                            }
                            function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                                uint256 newAllowance = token.allowance(address(this), spender) + value;
                                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                            }
                            function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                                unchecked {
                                    uint256 oldAllowance = token.allowance(address(this), spender);
                                    require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                                    uint256 newAllowance = oldAllowance - value;
                                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                                }
                            }
                            /**
                             * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                             * on the return value: the return value is optional (but if data is returned, it must not be false).
                             * @param token The token targeted by the call.
                             * @param data The call data (encoded using abi.encode or one of its variants).
                             */
                            function _callOptionalReturn(IERC20 token, bytes memory data) private {
                                // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                                // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                                // the target address contains contract code and also asserts for success in the low-level call.
                                bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                                if (returndata.length > 0) {
                                    // Return data is optional
                                    require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                                }
                            }
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        interface IWETH {
                            function deposit() external payable;
                            function withdraw(uint256 amount) external;
                            function transfer(address to, uint256 value) external returns (bool);
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.1;
                        /// @author thirdweb, OpenZeppelin Contracts (v4.9.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
                             *
                             * Furthermore, `isContract` will also return true if the target contract within
                             * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                             * which only has an effect at the end of a transaction.
                             * ====
                             *
                             * [IMPORTANT]
                             * ====
                             * You shouldn't rely on `isContract` to protect against flash loan attacks!
                             *
                             * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                             * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                             * constructor.
                             * ====
                             */
                            function isContract(address account) internal view returns (bool) {
                                // This method relies on extcodesize/address.code.length, which returns 0
                                // for contracts in construction, since the code is only stored at the end
                                // of the constructor execution.
                                return account.code.length > 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://consensys.net/diligence/blog/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.8.0/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 functionCallWithValue(target, data, 0, "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");
                                (bool success, bytes memory returndata) = target.call{ value: value }(data);
                                return verifyCallResultFromTarget(target, 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) {
                                (bool success, bytes memory returndata) = target.staticcall(data);
                                return verifyCallResultFromTarget(target, 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) {
                                (bool success, bytes memory returndata) = target.delegatecall(data);
                                return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                            }
                            /**
                             * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                             * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                             *
                             * _Available since v4.8._
                             */
                            function verifyCallResultFromTarget(
                                address target,
                                bool success,
                                bytes memory returndata,
                                string memory errorMessage
                            ) internal view returns (bytes memory) {
                                if (success) {
                                    if (returndata.length == 0) {
                                        // only check isContract if the call was successful and the return data is empty
                                        // otherwise we already know that it was a contract
                                        require(isContract(target), "Address: call to non-contract");
                                    }
                                    return returndata;
                                } else {
                                    _revert(returndata, errorMessage);
                                }
                            }
                            /**
                             * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                             * revert reason or 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 {
                                    _revert(returndata, errorMessage);
                                }
                            }
                            function _revert(bytes memory returndata, string memory errorMessage) private pure {
                                // 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
                                    /// @solidity memory-safe-assembly
                                    assembly {
                                        let returndata_size := mload(returndata)
                                        revert(add(32, returndata), returndata_size)
                                    }
                                } else {
                                    revert(errorMessage);
                                }
                            }
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        // Helper interfaces
                        import { IWETH } from "../infra/interface/IWETH.sol";
                        import { SafeERC20, IERC20 } from "../external-deps/openzeppelin/token/ERC20/utils/SafeERC20.sol";
                        library CurrencyTransferLib {
                            using SafeERC20 for IERC20;
                            error CurrencyTransferLibMismatchedValue(uint256 expected, uint256 actual);
                            error CurrencyTransferLibFailedNativeTransfer(address recipient, uint256 value);
                            /// @dev The address interpreted as native token of the chain.
                            address public constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                            /// @dev Transfers a given amount of currency.
                            function transferCurrency(address _currency, address _from, address _to, uint256 _amount) internal {
                                if (_amount == 0) {
                                    return;
                                }
                                if (_currency == NATIVE_TOKEN) {
                                    safeTransferNativeToken(_to, _amount);
                                } else {
                                    safeTransferERC20(_currency, _from, _to, _amount);
                                }
                            }
                            /// @dev Transfers a given amount of currency. (With native token wrapping)
                            function transferCurrencyWithWrapper(
                                address _currency,
                                address _from,
                                address _to,
                                uint256 _amount,
                                address _nativeTokenWrapper
                            ) internal {
                                if (_amount == 0) {
                                    return;
                                }
                                if (_currency == NATIVE_TOKEN) {
                                    if (_from == address(this)) {
                                        // withdraw from weth then transfer withdrawn native token to recipient
                                        IWETH(_nativeTokenWrapper).withdraw(_amount);
                                        safeTransferNativeTokenWithWrapper(_to, _amount, _nativeTokenWrapper);
                                    } else if (_to == address(this)) {
                                        // store native currency in weth
                                        if (_amount != msg.value) {
                                            revert CurrencyTransferLibMismatchedValue(msg.value, _amount);
                                        }
                                        IWETH(_nativeTokenWrapper).deposit{ value: _amount }();
                                    } else {
                                        safeTransferNativeTokenWithWrapper(_to, _amount, _nativeTokenWrapper);
                                    }
                                } else {
                                    safeTransferERC20(_currency, _from, _to, _amount);
                                }
                            }
                            /// @dev Transfer `amount` of ERC20 token from `from` to `to`.
                            function safeTransferERC20(address _currency, address _from, address _to, uint256 _amount) internal {
                                if (_from == _to) {
                                    return;
                                }
                                if (_from == address(this)) {
                                    IERC20(_currency).safeTransfer(_to, _amount);
                                } else {
                                    IERC20(_currency).safeTransferFrom(_from, _to, _amount);
                                }
                            }
                            /// @dev Transfers `amount` of native token to `to`.
                            function safeTransferNativeToken(address to, uint256 value) internal {
                                // solhint-disable avoid-low-level-calls
                                // slither-disable-next-line low-level-calls
                                (bool success, ) = to.call{ value: value }("");
                                if (!success) {
                                    revert CurrencyTransferLibFailedNativeTransfer(to, value);
                                }
                            }
                            /// @dev Transfers `amount` of native token to `to`. (With native token wrapping)
                            function safeTransferNativeTokenWithWrapper(address to, uint256 value, address _nativeTokenWrapper) internal {
                                // solhint-disable avoid-low-level-calls
                                // slither-disable-next-line low-level-calls
                                (bool success, ) = to.call{ value: value }("");
                                if (!success) {
                                    IWETH(_nativeTokenWrapper).deposit{ value: value }();
                                    IERC20(_nativeTokenWrapper).safeTransfer(to, value);
                                }
                            }
                        }
                        // SPDX-License-Identifier: Apache 2.0
                        pragma solidity ^0.8.0;
                        /// @author OpenZeppelin, thirdweb
                        library MerkleProof {
                            function verify(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool, uint256) {
                                bytes32 computedHash = leaf;
                                uint256 index = 0;
                                for (uint256 i = 0; i < proof.length; i++) {
                                    index *= 2;
                                    bytes32 proofElement = proof[i];
                                    if (computedHash <= proofElement) {
                                        // Hash(current computed hash + current element of the proof)
                                        computedHash = _efficientHash(computedHash, proofElement);
                                    } else {
                                        // Hash(current element of the proof + current computed hash)
                                        computedHash = _efficientHash(proofElement, computedHash);
                                        index += 1;
                                    }
                                }
                                // Check if the computed hash (root) is equal to the provided root
                                return (computedHash == root, index);
                            }
                            /**
                             * @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
                             */
                            function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
                                /// @solidity memory-safe-assembly
                                assembly {
                                    mstore(0x00, a)
                                    mstore(0x20, b)
                                    value := keccak256(0x00, 0x40)
                                }
                            }
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.0;
                        /// @author thirdweb
                        /**
                         * @dev String operations.
                         */
                        library Strings {
                            bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
                            /**
                             * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                             */
                            function toString(uint256 value) internal pure returns (string memory) {
                                // Inspired by OraclizeAPI's implementation - MIT licence
                                // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
                                if (value == 0) {
                                    return "0";
                                }
                                uint256 temp = value;
                                uint256 digits;
                                while (temp != 0) {
                                    digits++;
                                    temp /= 10;
                                }
                                bytes memory buffer = new bytes(digits);
                                while (value != 0) {
                                    digits -= 1;
                                    buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                                    value /= 10;
                                }
                                return string(buffer);
                            }
                            /**
                             * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                             */
                            function toHexString(uint256 value) internal pure returns (string memory) {
                                if (value == 0) {
                                    return "0x00";
                                }
                                uint256 temp = value;
                                uint256 length = 0;
                                while (temp != 0) {
                                    length++;
                                    temp >>= 8;
                                }
                                return toHexString(value, length);
                            }
                            /**
                             * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                             */
                            function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                                bytes memory buffer = new bytes(2 * length + 2);
                                buffer[0] = "0";
                                buffer[1] = "x";
                                for (uint256 i = 2 * length + 1; i > 1; --i) {
                                    buffer[i] = _HEX_SYMBOLS[value & 0xf];
                                    value >>= 4;
                                }
                                require(value == 0, "Strings: hex length insufficient");
                                return string(buffer);
                            }
                            /// @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.
                                }
                            }
                        }
                        // SPDX-License-Identifier: Apache-2.0
                        pragma solidity ^0.8.11;
                        /// @author thirdweb
                        //   $$\\     $$\\       $$\\                 $$\\                         $$\\
                        //   $$ |    $$ |      \\__|                $$ |                        $$ |
                        // $$$$$$\\   $$$$$$$\\  $$\\  $$$$$$\\   $$$$$$$ |$$\\  $$\\  $$\\  $$$$$$\\  $$$$$$$\\
                        // \\_$$  _|  $$  __$$\\ $$ |$$  __$$\\ $$  __$$ |$$ | $$ | $$ |$$  __$$\\ $$  __$$\\
                        //   $$ |    $$ |  $$ |$$ |$$ |  \\__|$$ /  $$ |$$ | $$ | $$ |$$$$$$$$ |$$ |  $$ |
                        //   $$ |$$\\ $$ |  $$ |$$ |$$ |      $$ |  $$ |$$ | $$ | $$ |$$   ____|$$ |  $$ |
                        //   \\$$$$  |$$ |  $$ |$$ |$$ |      \\$$$$$$$ |\\$$$$$\\$$$$  |\\$$$$$$$\\ $$$$$$$  |
                        //    \\____/ \\__|  \\__|\\__|\\__|       \\_______| \\_____\\____/  \\_______|\\_______/
                        //  ==========  External imports    ==========
                        import "../../extension/Multicall.sol";
                        import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";
                        import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol";
                        import "../../eip/ERC721AVirtualApproveUpgradeable.sol";
                        //  ==========  Internal imports    ==========
                        import "../../external-deps/openzeppelin/metatx/ERC2771ContextUpgradeable.sol";
                        import "../../lib/CurrencyTransferLib.sol";
                        //  ==========  Features    ==========
                        import "../../extension/ContractMetadata.sol";
                        import "../../extension/PlatformFee.sol";
                        import "../../extension/Royalty.sol";
                        import "../../extension/PrimarySale.sol";
                        import "../../extension/Ownable.sol";
                        import "../../extension/DelayedReveal.sol";
                        import "../../extension/LazyMint.sol";
                        import "../../extension/PermissionsEnumerable.sol";
                        import "../../extension/Drop.sol";
                        contract DropERC721 is
                            Initializable,
                            ContractMetadata,
                            PlatformFee,
                            Royalty,
                            PrimarySale,
                            Ownable,
                            DelayedReveal,
                            LazyMint,
                            PermissionsEnumerable,
                            Drop,
                            ERC2771ContextUpgradeable,
                            Multicall,
                            ERC721AUpgradeable
                        {
                            using StringsUpgradeable for uint256;
                            /*///////////////////////////////////////////////////////////////
                                                    State variables
                            //////////////////////////////////////////////////////////////*/
                            /// @dev Only transfers to or from TRANSFER_ROLE holders are valid, when transfers are restricted.
                            bytes32 private transferRole;
                            /// @dev Only MINTER_ROLE holders can sign off on `MintRequest`s and lazy mint tokens.
                            bytes32 private minterRole;
                            /// @dev Only METADATA_ROLE holders can reveal the URI for a batch of delayed reveal NFTs, and update or freeze batch metadata.
                            bytes32 private metadataRole;
                            /// @dev Max bps in the thirdweb system.
                            uint256 private constant MAX_BPS = 10_000;
                            address public constant DEFAULT_FEE_RECIPIENT = 0x1Af20C6B23373350aD464700B5965CE4B0D2aD94;
                            uint16 private constant DEFAULT_FEE_BPS = 50;
                            /// @dev Global max total supply of NFTs.
                            uint256 public maxTotalSupply;
                            /// @dev Emitted when the global max supply of tokens is updated.
                            event MaxTotalSupplyUpdated(uint256 maxTotalSupply);
                            /*///////////////////////////////////////////////////////////////
                                            Constructor + initializer logic
                            //////////////////////////////////////////////////////////////*/
                            constructor() initializer {}
                            /// @dev Initializes the contract, like a constructor.
                            function initialize(
                                address _defaultAdmin,
                                string memory _name,
                                string memory _symbol,
                                string memory _contractURI,
                                address[] memory _trustedForwarders,
                                address _saleRecipient,
                                address _royaltyRecipient,
                                uint128 _royaltyBps,
                                uint128 _platformFeeBps,
                                address _platformFeeRecipient
                            ) external initializer {
                                bytes32 _transferRole = keccak256("TRANSFER_ROLE");
                                bytes32 _minterRole = keccak256("MINTER_ROLE");
                                bytes32 _metadataRole = keccak256("METADATA_ROLE");
                                // Initialize inherited contracts, most base-like -> most derived.
                                __ERC2771Context_init(_trustedForwarders);
                                __ERC721A_init(_name, _symbol);
                                _setupContractURI(_contractURI);
                                _setupOwner(_defaultAdmin);
                                _setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin);
                                _setupRole(_minterRole, _defaultAdmin);
                                _setupRole(_transferRole, _defaultAdmin);
                                _setupRole(_transferRole, address(0));
                                _setupRole(_metadataRole, _defaultAdmin);
                                _setRoleAdmin(_metadataRole, _metadataRole);
                                _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps);
                                _setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps);
                                _setupPrimarySaleRecipient(_saleRecipient);
                                transferRole = _transferRole;
                                minterRole = _minterRole;
                                metadataRole = _metadataRole;
                            }
                            /*///////////////////////////////////////////////////////////////
                                                ERC 165 / 721 / 2981 logic
                            //////////////////////////////////////////////////////////////*/
                            /// @dev Returns the URI for a given tokenId.
                            function tokenURI(uint256 _tokenId) public view override returns (string memory) {
                                (uint256 batchId, ) = _getBatchId(_tokenId);
                                string memory batchUri = _getBaseURI(_tokenId);
                                if (isEncryptedBatch(batchId)) {
                                    return string(abi.encodePacked(batchUri, "0"));
                                } else {
                                    return string(abi.encodePacked(batchUri, _tokenId.toString()));
                                }
                            }
                            /// @dev See ERC 165
                            function supportsInterface(
                                bytes4 interfaceId
                            ) public view virtual override(ERC721AUpgradeable, IERC165) returns (bool) {
                                return super.supportsInterface(interfaceId) || type(IERC2981Upgradeable).interfaceId == interfaceId;
                            }
                            /*///////////////////////////////////////////////////////////////
                                                Contract identifiers
                            //////////////////////////////////////////////////////////////*/
                            function contractType() external pure returns (bytes32) {
                                return bytes32("DropERC721");
                            }
                            function contractVersion() external pure returns (uint8) {
                                return uint8(4);
                            }
                            /*///////////////////////////////////////////////////////////////
                                            Lazy minting + delayed-reveal logic
                            //////////////////////////////////////////////////////////////*/
                            /**
                             *  @dev Lets an account with `MINTER_ROLE` lazy mint 'n' NFTs.
                             *       The URIs for each token is the provided `_baseURIForTokens` + `{tokenId}`.
                             */
                            function lazyMint(
                                uint256 _amount,
                                string calldata _baseURIForTokens,
                                bytes calldata _data
                            ) public override returns (uint256 batchId) {
                                if (_data.length > 0) {
                                    (bytes memory encryptedURI, bytes32 provenanceHash) = abi.decode(_data, (bytes, bytes32));
                                    if (encryptedURI.length != 0 && provenanceHash != "") {
                                        _setEncryptedData(nextTokenIdToLazyMint + _amount, _data);
                                    }
                                }
                                return super.lazyMint(_amount, _baseURIForTokens, _data);
                            }
                            /// @dev Lets an account with `METADATA_ROLE` reveal the URI for a batch of 'delayed-reveal' NFTs.
                            /// @param _index the ID of a token with the desired batch.
                            /// @param _key the key to decrypt the batch's URI.
                            function reveal(
                                uint256 _index,
                                bytes calldata _key
                            ) external onlyRole(metadataRole) returns (string memory revealedURI) {
                                uint256 batchId = getBatchIdAtIndex(_index);
                                revealedURI = getRevealURI(batchId, _key);
                                _setEncryptedData(batchId, "");
                                _setBaseURI(batchId, revealedURI);
                                emit TokenURIRevealed(_index, revealedURI);
                            }
                            /**
                             * @notice Updates the base URI for a batch of tokens. Can only be called if the batch has been revealed/is not encrypted.
                             *
                             * @param _index Index of the desired batch in batchIds array
                             * @param _uri   the new base URI for the batch.
                             */
                            function updateBatchBaseURI(uint256 _index, string calldata _uri) external onlyRole(metadataRole) {
                                require(!isEncryptedBatch(getBatchIdAtIndex(_index)), "Encrypted batch");
                                uint256 batchId = getBatchIdAtIndex(_index);
                                _setBaseURI(batchId, _uri);
                            }
                            /**
                             * @notice Freezes the base URI for a batch of tokens.
                             *
                             * @param _index Index of the desired batch in batchIds array.
                             */
                            function freezeBatchBaseURI(uint256 _index) external onlyRole(metadataRole) {
                                require(!isEncryptedBatch(getBatchIdAtIndex(_index)), "Encrypted batch");
                                uint256 batchId = getBatchIdAtIndex(_index);
                                _freezeBaseURI(batchId);
                            }
                            /*///////////////////////////////////////////////////////////////
                                                Setter functions
                            //////////////////////////////////////////////////////////////*/
                            /// @dev Lets a contract admin set the global maximum supply for collection's NFTs.
                            function setMaxTotalSupply(uint256 _maxTotalSupply) external onlyRole(DEFAULT_ADMIN_ROLE) {
                                maxTotalSupply = _maxTotalSupply;
                                emit MaxTotalSupplyUpdated(_maxTotalSupply);
                            }
                            /*///////////////////////////////////////////////////////////////
                                                Internal functions
                            //////////////////////////////////////////////////////////////*/
                            /// @dev Runs before every `claim` function call.
                            function _beforeClaim(
                                address,
                                uint256 _quantity,
                                address,
                                uint256,
                                AllowlistProof calldata,
                                bytes memory
                            ) internal view override {
                                require(_currentIndex + _quantity <= nextTokenIdToLazyMint, "!Tokens");
                                require(maxTotalSupply == 0 || _currentIndex + _quantity <= maxTotalSupply, "!Supply");
                            }
                            /// @dev Collects and distributes the primary sale value of NFTs being claimed.
                            function _collectPriceOnClaim(
                                address _primarySaleRecipient,
                                uint256 _quantityToClaim,
                                address _currency,
                                uint256 _pricePerToken
                            ) internal override {
                                if (_pricePerToken == 0) {
                                    require(msg.value == 0, "!V");
                                    return;
                                }
                                (address platformFeeRecipient, uint16 platformFeeBps) = getPlatformFeeInfo();
                                address saleRecipient = _primarySaleRecipient == address(0) ? primarySaleRecipient() : _primarySaleRecipient;
                                uint256 totalPrice = _quantityToClaim * _pricePerToken;
                                uint256 platformFeesTw = (totalPrice * DEFAULT_FEE_BPS) / MAX_BPS;
                                uint256 platformFees = (totalPrice * platformFeeBps) / MAX_BPS;
                                bool validMsgValue;
                                if (_currency == CurrencyTransferLib.NATIVE_TOKEN) {
                                    validMsgValue = msg.value == totalPrice;
                                } else {
                                    validMsgValue = msg.value == 0;
                                }
                                require(validMsgValue, "!V");
                                CurrencyTransferLib.transferCurrency(_currency, _msgSender(), DEFAULT_FEE_RECIPIENT, platformFeesTw);
                                CurrencyTransferLib.transferCurrency(_currency, _msgSender(), platformFeeRecipient, platformFees);
                                CurrencyTransferLib.transferCurrency(
                                    _currency,
                                    _msgSender(),
                                    saleRecipient,
                                    totalPrice - platformFees - platformFeesTw
                                );
                            }
                            /// @dev Transfers the NFTs being claimed.
                            function _transferTokensOnClaim(
                                address _to,
                                uint256 _quantityBeingClaimed
                            ) internal override returns (uint256 startTokenId) {
                                startTokenId = _currentIndex;
                                _safeMint(_to, _quantityBeingClaimed);
                            }
                            /// @dev Checks whether platform fee info can be set in the given execution context.
                            function _canSetPlatformFeeInfo() internal view override returns (bool) {
                                return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                            }
                            /// @dev Checks whether primary sale recipient can be set in the given execution context.
                            function _canSetPrimarySaleRecipient() internal view override returns (bool) {
                                return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                            }
                            /// @dev Checks whether owner can be set in the given execution context.
                            function _canSetOwner() internal view override returns (bool) {
                                return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                            }
                            /// @dev Checks whether royalty info can be set in the given execution context.
                            function _canSetRoyaltyInfo() internal view override returns (bool) {
                                return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                            }
                            /// @dev Checks whether contract metadata can be set in the given execution context.
                            function _canSetContractURI() internal view override returns (bool) {
                                return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                            }
                            /// @dev Checks whether platform fee info can be set in the given execution context.
                            function _canSetClaimConditions() internal view override returns (bool) {
                                return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                            }
                            /// @dev Returns whether lazy minting can be done in the given execution context.
                            function _canLazyMint() internal view virtual override returns (bool) {
                                return hasRole(minterRole, _msgSender());
                            }
                            /*///////////////////////////////////////////////////////////////
                                                Miscellaneous
                            //////////////////////////////////////////////////////////////*/
                            /**
                             * Returns the total amount of tokens minted in the contract.
                             */
                            function totalMinted() external view returns (uint256) {
                                return _totalMinted();
                            }
                            /// @dev The tokenId of the next NFT that will be minted / lazy minted.
                            function nextTokenIdToMint() external view returns (uint256) {
                                return nextTokenIdToLazyMint;
                            }
                            /// @dev The next token ID of the NFT that can be claimed.
                            function nextTokenIdToClaim() external view returns (uint256) {
                                return _currentIndex;
                            }
                            /// @dev Burns `tokenId`. See {ERC721-_burn}.
                            function burn(uint256 tokenId) external virtual {
                                // note: ERC721AUpgradeable's `_burn(uint256,bool)` internally checks for token approvals.
                                _burn(tokenId, true);
                            }
                            /// @dev See {ERC721-_beforeTokenTransfer}.
                            function _beforeTokenTransfers(
                                address from,
                                address to,
                                uint256 startTokenId,
                                uint256 quantity
                            ) internal virtual override {
                                super._beforeTokenTransfers(from, to, startTokenId, quantity);
                                // if transfer is restricted on the contract, we still want to allow burning and minting
                                if (!hasRole(transferRole, address(0)) && from != address(0) && to != address(0)) {
                                    if (!hasRole(transferRole, from) && !hasRole(transferRole, to)) {
                                        revert("!Transfer-Role");
                                    }
                                }
                            }
                            function _dropMsgSender() internal view virtual override returns (address) {
                                return _msgSender();
                            }
                            function _msgSender()
                                internal
                                view
                                virtual
                                override(ContextUpgradeable, ERC2771ContextUpgradeable, Multicall)
                                returns (address sender)
                            {
                                return ERC2771ContextUpgradeable._msgSender();
                            }
                            function _msgData()
                                internal
                                view
                                virtual
                                override(ContextUpgradeable, ERC2771ContextUpgradeable)
                                returns (bytes calldata)
                            {
                                return ERC2771ContextUpgradeable._msgData();
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        // ERC721A Contracts v3.3.0
                        // Creator: Chiru Labs
                        pragma solidity ^0.8.4;
                        import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
                        import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721MetadataUpgradeable.sol";
                        /**
                         * @dev Interface of an ERC721A compliant contract.
                         */
                        interface IERC721AUpgradeable is IERC721Upgradeable, IERC721MetadataUpgradeable {
                            /**
                             * The caller must own the token or be an approved operator.
                             */
                            error ApprovalCallerNotOwnerNorApproved();
                            /**
                             * The token does not exist.
                             */
                            error ApprovalQueryForNonexistentToken();
                            /**
                             * The caller cannot approve to their own address.
                             */
                            error ApproveToCaller();
                            /**
                             * The caller cannot approve to the current owner.
                             */
                            error ApprovalToCurrentOwner();
                            /**
                             * Cannot query the balance for the zero address.
                             */
                            error BalanceQueryForZeroAddress();
                            /**
                             * Cannot mint to the zero address.
                             */
                            error MintToZeroAddress();
                            /**
                             * The quantity of tokens minted must be more than zero.
                             */
                            error MintZeroQuantity();
                            /**
                             * The token does not exist.
                             */
                            error OwnerQueryForNonexistentToken();
                            /**
                             * The caller must own the token or be an approved operator.
                             */
                            error TransferCallerNotOwnerNorApproved();
                            /**
                             * The token must be owned by `from`.
                             */
                            error TransferFromIncorrectOwner();
                            /**
                             * Cannot safely transfer to a contract that does not implement the ERC721Receiver interface.
                             */
                            error TransferToNonERC721ReceiverImplementer();
                            /**
                             * Cannot transfer to the zero address.
                             */
                            error TransferToZeroAddress();
                            /**
                             * The token does not exist.
                             */
                            error URIQueryForNonexistentToken();
                            // Compiler will pack this into a single 256bit word.
                            struct TokenOwnership {
                                // The address of the owner.
                                address addr;
                                // Keeps track of the start time of ownership with minimal overhead for tokenomics.
                                uint64 startTimestamp;
                                // Whether the token has been burned.
                                bool burned;
                            }
                            // Compiler will pack this into a single 256bit word.
                            struct AddressData {
                                // Realistically, 2**64-1 is more than enough.
                                uint64 balance;
                                // Keeps track of mint count with minimal overhead for tokenomics.
                                uint64 numberMinted;
                                // Keeps track of burn count with minimal overhead for tokenomics.
                                uint64 numberBurned;
                                // For miscellaneous variable(s) pertaining to the address
                                // (e.g. number of whitelist mint slots used).
                                // If there are multiple variables, please pack them into a uint64.
                                uint64 aux;
                            }
                            /**
                             * @dev Returns the total amount of tokens stored by the contract.
                             * 
                             * Burned tokens are calculated here, use `_totalMinted()` if you want to count just minted tokens.
                             */
                            function totalSupply() external view returns (uint256);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC2981.sol)
                        pragma solidity ^0.8.0;
                        import "../utils/introspection/IERC165Upgradeable.sol";
                        /**
                         * @dev Interface for the NFT Royalty Standard.
                         *
                         * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
                         * support for royalty payments across all NFT marketplaces and ecosystem participants.
                         *
                         * _Available since v4.5._
                         */
                        interface IERC2981Upgradeable is IERC165Upgradeable {
                            /**
                             * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
                             * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
                             */
                            function royaltyInfo(
                                uint256 tokenId,
                                uint256 salePrice
                            ) external view returns (address receiver, uint256 royaltyAmount);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
                        pragma solidity ^0.8.2;
                        import "../../utils/AddressUpgradeable.sol";
                        /**
                         * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
                         * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
                         * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
                         * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
                         *
                         * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
                         * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
                         * case an upgrade adds a module that needs to be initialized.
                         *
                         * For example:
                         *
                         * [.hljs-theme-light.nopadding]
                         * ```solidity
                         * contract MyToken is ERC20Upgradeable {
                         *     function initialize() initializer public {
                         *         __ERC20_init("MyToken", "MTK");
                         *     }
                         * }
                         *
                         * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
                         *     function initializeV2() reinitializer(2) public {
                         *         __ERC20Permit_init("MyToken");
                         *     }
                         * }
                         * ```
                         *
                         * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
                         * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
                         *
                         * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
                         * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
                         *
                         * [CAUTION]
                         * ====
                         * Avoid leaving a contract uninitialized.
                         *
                         * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
                         * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
                         * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
                         *
                         * [.hljs-theme-light.nopadding]
                         * ```
                         * /// @custom:oz-upgrades-unsafe-allow constructor
                         * constructor() {
                         *     _disableInitializers();
                         * }
                         * ```
                         * ====
                         */
                        abstract contract Initializable {
                            /**
                             * @dev Indicates that the contract has been initialized.
                             * @custom:oz-retyped-from bool
                             */
                            uint8 private _initialized;
                            /**
                             * @dev Indicates that the contract is in the process of being initialized.
                             */
                            bool private _initializing;
                            /**
                             * @dev Triggered when the contract has been initialized or reinitialized.
                             */
                            event Initialized(uint8 version);
                            /**
                             * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
                             * `onlyInitializing` functions can be used to initialize parent contracts.
                             *
                             * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
                             * constructor.
                             *
                             * Emits an {Initialized} event.
                             */
                            modifier initializer() {
                                bool isTopLevelCall = !_initializing;
                                require(
                                    (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                                    "Initializable: contract is already initialized"
                                );
                                _initialized = 1;
                                if (isTopLevelCall) {
                                    _initializing = true;
                                }
                                _;
                                if (isTopLevelCall) {
                                    _initializing = false;
                                    emit Initialized(1);
                                }
                            }
                            /**
                             * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
                             * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
                             * used to initialize parent contracts.
                             *
                             * A reinitializer may be used after the original initialization step. This is essential to configure modules that
                             * are added through upgrades and that require initialization.
                             *
                             * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
                             * cannot be nested. If one is invoked in the context of another, execution will revert.
                             *
                             * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
                             * a contract, executing them in the right order is up to the developer or operator.
                             *
                             * WARNING: setting the version to 255 will prevent any future reinitialization.
                             *
                             * Emits an {Initialized} event.
                             */
                            modifier reinitializer(uint8 version) {
                                require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
                                _initialized = version;
                                _initializing = true;
                                _;
                                _initializing = false;
                                emit Initialized(version);
                            }
                            /**
                             * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
                             * {initializer} and {reinitializer} modifiers, directly or indirectly.
                             */
                            modifier onlyInitializing() {
                                require(_initializing, "Initializable: contract is not initializing");
                                _;
                            }
                            /**
                             * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
                             * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
                             * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
                             * through proxies.
                             *
                             * Emits an {Initialized} event the first time it is successfully executed.
                             */
                            function _disableInitializers() internal virtual {
                                require(!_initializing, "Initializable: contract is initializing");
                                if (_initialized != type(uint8).max) {
                                    _initialized = type(uint8).max;
                                    emit Initialized(type(uint8).max);
                                }
                            }
                            /**
                             * @dev Returns the highest version that has been initialized. See {reinitializer}.
                             */
                            function _getInitializedVersion() internal view returns (uint8) {
                                return _initialized;
                            }
                            /**
                             * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
                             */
                            function _isInitializing() internal view returns (bool) {
                                return _initializing;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v4.6.0) (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 IERC721ReceiverUpgradeable {
                            /**
                             * @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 `IERC721Receiver.onERC721Received.selector`.
                             */
                            function onERC721Received(
                                address operator,
                                address from,
                                uint256 tokenId,
                                bytes calldata data
                            ) external returns (bytes4);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)
                        pragma solidity ^0.8.0;
                        import "../../utils/introspection/IERC165Upgradeable.sol";
                        /**
                         * @dev Required interface of an ERC721 compliant contract.
                         */
                        interface IERC721Upgradeable is IERC165Upgradeable {
                            /**
                             * @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`.
                             *
                             * 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;
                            /**
                             * @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 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: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
                             * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
                             * understand this adds an external call which potentially creates a reentrancy vulnerability.
                             *
                             * 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 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 the account approved for `tokenId` token.
                             *
                             * Requirements:
                             *
                             * - `tokenId` must exist.
                             */
                            function getApproved(uint256 tokenId) external view returns (address operator);
                            /**
                             * @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);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
                        pragma solidity ^0.8.0;
                        import "../IERC721Upgradeable.sol";
                        /**
                         * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
                         * @dev See https://eips.ethereum.org/EIPS/eip-721
                         */
                        interface IERC721MetadataUpgradeable is IERC721Upgradeable {
                            /**
                             * @dev Returns the token collection name.
                             */
                            function name() external view returns (string memory);
                            /**
                             * @dev Returns the token collection symbol.
                             */
                            function symbol() external view returns (string memory);
                            /**
                             * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
                             */
                            function tokenURI(uint256 tokenId) external view returns (string memory);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
                        pragma solidity ^0.8.1;
                        /**
                         * @dev Collection of functions related to the address type
                         */
                        library AddressUpgradeable {
                            /**
                             * @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
                             *
                             * Furthermore, `isContract` will also return true if the target contract within
                             * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                             * which only has an effect at the end of a transaction.
                             * ====
                             *
                             * [IMPORTANT]
                             * ====
                             * You shouldn't rely on `isContract` to protect against flash loan attacks!
                             *
                             * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                             * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                             * constructor.
                             * ====
                             */
                            function isContract(address account) internal view returns (bool) {
                                // This method relies on extcodesize/address.code.length, which returns 0
                                // for contracts in construction, since the code is only stored at the end
                                // of the constructor execution.
                                return account.code.length > 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://consensys.net/diligence/blog/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.8.0/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 functionCallWithValue(target, data, 0, "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");
                                (bool success, bytes memory returndata) = target.call{value: value}(data);
                                return verifyCallResultFromTarget(target, 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) {
                                (bool success, bytes memory returndata) = target.staticcall(data);
                                return verifyCallResultFromTarget(target, 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) {
                                (bool success, bytes memory returndata) = target.delegatecall(data);
                                return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                            }
                            /**
                             * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                             * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                             *
                             * _Available since v4.8._
                             */
                            function verifyCallResultFromTarget(
                                address target,
                                bool success,
                                bytes memory returndata,
                                string memory errorMessage
                            ) internal view returns (bytes memory) {
                                if (success) {
                                    if (returndata.length == 0) {
                                        // only check isContract if the call was successful and the return data is empty
                                        // otherwise we already know that it was a contract
                                        require(isContract(target), "Address: call to non-contract");
                                    }
                                    return returndata;
                                } else {
                                    _revert(returndata, errorMessage);
                                }
                            }
                            /**
                             * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                             * revert reason or 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 {
                                    _revert(returndata, errorMessage);
                                }
                            }
                            function _revert(bytes memory returndata, string memory errorMessage) private pure {
                                // 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
                                    /// @solidity memory-safe-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/Context.sol)
                        pragma solidity ^0.8.0;
                        import "../proxy/utils/Initializable.sol";
                        /**
                         * @dev Provides information about the current execution context, including the
                         * sender of the transaction and its data. While these are generally available
                         * via msg.sender and msg.data, they should not be accessed in such a direct
                         * manner, since when dealing with meta-transactions the account sending and
                         * paying for execution may not be the actual sender (as far as an application
                         * is concerned).
                         *
                         * This contract is only required for intermediate, library-like contracts.
                         */
                        abstract contract ContextUpgradeable is Initializable {
                            function __Context_init() internal onlyInitializing {
                            }
                            function __Context_init_unchained() internal onlyInitializing {
                            }
                            function _msgSender() internal view virtual returns (address) {
                                return msg.sender;
                            }
                            function _msgData() internal view virtual returns (bytes calldata) {
                                return msg.data;
                            }
                            /**
                             * @dev This empty reserved space is put in place to allow future versions to add new
                             * variables without shifting down storage in the inheritance chain.
                             * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                             */
                            uint256[50] private __gap;
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
                        pragma solidity ^0.8.0;
                        import "./math/MathUpgradeable.sol";
                        import "./math/SignedMathUpgradeable.sol";
                        /**
                         * @dev String operations.
                         */
                        library StringsUpgradeable {
                            bytes16 private constant _SYMBOLS = "0123456789abcdef";
                            uint8 private constant _ADDRESS_LENGTH = 20;
                            /**
                             * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                             */
                            function toString(uint256 value) internal pure returns (string memory) {
                                unchecked {
                                    uint256 length = MathUpgradeable.log10(value) + 1;
                                    string memory buffer = new string(length);
                                    uint256 ptr;
                                    /// @solidity memory-safe-assembly
                                    assembly {
                                        ptr := add(buffer, add(32, length))
                                    }
                                    while (true) {
                                        ptr--;
                                        /// @solidity memory-safe-assembly
                                        assembly {
                                            mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                                        }
                                        value /= 10;
                                        if (value == 0) break;
                                    }
                                    return buffer;
                                }
                            }
                            /**
                             * @dev Converts a `int256` to its ASCII `string` decimal representation.
                             */
                            function toString(int256 value) internal pure returns (string memory) {
                                return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMathUpgradeable.abs(value))));
                            }
                            /**
                             * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                             */
                            function toHexString(uint256 value) internal pure returns (string memory) {
                                unchecked {
                                    return toHexString(value, MathUpgradeable.log256(value) + 1);
                                }
                            }
                            /**
                             * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                             */
                            function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                                bytes memory buffer = new bytes(2 * length + 2);
                                buffer[0] = "0";
                                buffer[1] = "x";
                                for (uint256 i = 2 * length + 1; i > 1; --i) {
                                    buffer[i] = _SYMBOLS[value & 0xf];
                                    value >>= 4;
                                }
                                require(value == 0, "Strings: hex length insufficient");
                                return string(buffer);
                            }
                            /**
                             * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
                             */
                            function toHexString(address addr) internal pure returns (string memory) {
                                return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
                            }
                            /**
                             * @dev Returns true if the two strings are equal.
                             */
                            function equal(string memory a, string memory b) internal pure returns (bool) {
                                return keccak256(bytes(a)) == keccak256(bytes(b));
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
                        pragma solidity ^0.8.0;
                        import "./IERC165Upgradeable.sol";
                        import "../../proxy/utils/Initializable.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 ERC165Upgradeable is Initializable, IERC165Upgradeable {
                            function __ERC165_init() internal onlyInitializing {
                            }
                            function __ERC165_init_unchained() internal onlyInitializing {
                            }
                            /**
                             * @dev See {IERC165-supportsInterface}.
                             */
                            function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                                return interfaceId == type(IERC165Upgradeable).interfaceId;
                            }
                            /**
                             * @dev This empty reserved space is put in place to allow future versions to add new
                             * variables without shifting down storage in the inheritance chain.
                             * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                             */
                            uint256[50] private __gap;
                        }
                        // 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 IERC165Upgradeable {
                            /**
                             * @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 (last updated v4.9.0) (utils/math/Math.sol)
                        pragma solidity ^0.8.0;
                        /**
                         * @dev Standard math utilities missing in the Solidity language.
                         */
                        library MathUpgradeable {
                            enum Rounding {
                                Down, // Toward negative infinity
                                Up, // Toward infinity
                                Zero // Toward zero
                            }
                            /**
                             * @dev Returns the largest of two numbers.
                             */
                            function max(uint256 a, uint256 b) internal pure returns (uint256) {
                                return a > b ? a : b;
                            }
                            /**
                             * @dev Returns the smallest of two numbers.
                             */
                            function min(uint256 a, uint256 b) internal pure returns (uint256) {
                                return a < b ? a : b;
                            }
                            /**
                             * @dev Returns the average of two numbers. The result is rounded towards
                             * zero.
                             */
                            function average(uint256 a, uint256 b) internal pure returns (uint256) {
                                // (a + b) / 2 can overflow.
                                return (a & b) + (a ^ b) / 2;
                            }
                            /**
                             * @dev Returns the ceiling of the division of two numbers.
                             *
                             * This differs from standard division with `/` in that it rounds up instead
                             * of rounding down.
                             */
                            function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                                // (a + b - 1) / b can overflow on addition, so we distribute.
                                return a == 0 ? 0 : (a - 1) / b + 1;
                            }
                            /**
                             * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
                             * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
                             * with further edits by Uniswap Labs also under MIT license.
                             */
                            function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
                                unchecked {
                                    // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                                    // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                                    // variables such that product = prod1 * 2^256 + prod0.
                                    uint256 prod0; // Least significant 256 bits of the product
                                    uint256 prod1; // Most significant 256 bits of the product
                                    assembly {
                                        let mm := mulmod(x, y, not(0))
                                        prod0 := mul(x, y)
                                        prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                                    }
                                    // Handle non-overflow cases, 256 by 256 division.
                                    if (prod1 == 0) {
                                        // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                                        // The surrounding unchecked block does not change this fact.
                                        // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                                        return prod0 / denominator;
                                    }
                                    // Make sure the result is less than 2^256. Also prevents denominator == 0.
                                    require(denominator > prod1, "Math: mulDiv overflow");
                                    ///////////////////////////////////////////////
                                    // 512 by 256 division.
                                    ///////////////////////////////////////////////
                                    // Make division exact by subtracting the remainder from [prod1 prod0].
                                    uint256 remainder;
                                    assembly {
                                        // Compute remainder using mulmod.
                                        remainder := mulmod(x, y, denominator)
                                        // Subtract 256 bit number from 512 bit number.
                                        prod1 := sub(prod1, gt(remainder, prod0))
                                        prod0 := sub(prod0, remainder)
                                    }
                                    // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                                    // See https://cs.stackexchange.com/q/138556/92363.
                                    // Does not overflow because the denominator cannot be zero at this stage in the function.
                                    uint256 twos = denominator & (~denominator + 1);
                                    assembly {
                                        // Divide denominator by twos.
                                        denominator := div(denominator, twos)
                                        // Divide [prod1 prod0] by twos.
                                        prod0 := div(prod0, twos)
                                        // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                                        twos := add(div(sub(0, twos), twos), 1)
                                    }
                                    // Shift in bits from prod1 into prod0.
                                    prod0 |= prod1 * twos;
                                    // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                                    // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                                    // four bits. That is, denominator * inv = 1 mod 2^4.
                                    uint256 inverse = (3 * denominator) ^ 2;
                                    // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                                    // in modular arithmetic, doubling the correct bits in each step.
                                    inverse *= 2 - denominator * inverse; // inverse mod 2^8
                                    inverse *= 2 - denominator * inverse; // inverse mod 2^16
                                    inverse *= 2 - denominator * inverse; // inverse mod 2^32
                                    inverse *= 2 - denominator * inverse; // inverse mod 2^64
                                    inverse *= 2 - denominator * inverse; // inverse mod 2^128
                                    inverse *= 2 - denominator * inverse; // inverse mod 2^256
                                    // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                                    // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                                    // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                                    // is no longer required.
                                    result = prod0 * inverse;
                                    return result;
                                }
                            }
                            /**
                             * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
                             */
                            function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
                                uint256 result = mulDiv(x, y, denominator);
                                if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                                    result += 1;
                                }
                                return result;
                            }
                            /**
                             * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
                             *
                             * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
                             */
                            function sqrt(uint256 a) internal pure returns (uint256) {
                                if (a == 0) {
                                    return 0;
                                }
                                // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                                //
                                // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                                // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                                //
                                // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                                // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                                // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                                //
                                // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                                uint256 result = 1 << (log2(a) >> 1);
                                // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                                // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                                // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                                // into the expected uint128 result.
                                unchecked {
                                    result = (result + a / result) >> 1;
                                    result = (result + a / result) >> 1;
                                    result = (result + a / result) >> 1;
                                    result = (result + a / result) >> 1;
                                    result = (result + a / result) >> 1;
                                    result = (result + a / result) >> 1;
                                    result = (result + a / result) >> 1;
                                    return min(result, a / result);
                                }
                            }
                            /**
                             * @notice Calculates sqrt(a), following the selected rounding direction.
                             */
                            function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                                unchecked {
                                    uint256 result = sqrt(a);
                                    return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                                }
                            }
                            /**
                             * @dev Return the log in base 2, rounded down, of a positive value.
                             * Returns 0 if given 0.
                             */
                            function log2(uint256 value) internal pure returns (uint256) {
                                uint256 result = 0;
                                unchecked {
                                    if (value >> 128 > 0) {
                                        value >>= 128;
                                        result += 128;
                                    }
                                    if (value >> 64 > 0) {
                                        value >>= 64;
                                        result += 64;
                                    }
                                    if (value >> 32 > 0) {
                                        value >>= 32;
                                        result += 32;
                                    }
                                    if (value >> 16 > 0) {
                                        value >>= 16;
                                        result += 16;
                                    }
                                    if (value >> 8 > 0) {
                                        value >>= 8;
                                        result += 8;
                                    }
                                    if (value >> 4 > 0) {
                                        value >>= 4;
                                        result += 4;
                                    }
                                    if (value >> 2 > 0) {
                                        value >>= 2;
                                        result += 2;
                                    }
                                    if (value >> 1 > 0) {
                                        result += 1;
                                    }
                                }
                                return result;
                            }
                            /**
                             * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
                             * Returns 0 if given 0.
                             */
                            function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                                unchecked {
                                    uint256 result = log2(value);
                                    return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                                }
                            }
                            /**
                             * @dev Return the log in base 10, rounded down, of a positive value.
                             * Returns 0 if given 0.
                             */
                            function log10(uint256 value) internal pure returns (uint256) {
                                uint256 result = 0;
                                unchecked {
                                    if (value >= 10 ** 64) {
                                        value /= 10 ** 64;
                                        result += 64;
                                    }
                                    if (value >= 10 ** 32) {
                                        value /= 10 ** 32;
                                        result += 32;
                                    }
                                    if (value >= 10 ** 16) {
                                        value /= 10 ** 16;
                                        result += 16;
                                    }
                                    if (value >= 10 ** 8) {
                                        value /= 10 ** 8;
                                        result += 8;
                                    }
                                    if (value >= 10 ** 4) {
                                        value /= 10 ** 4;
                                        result += 4;
                                    }
                                    if (value >= 10 ** 2) {
                                        value /= 10 ** 2;
                                        result += 2;
                                    }
                                    if (value >= 10 ** 1) {
                                        result += 1;
                                    }
                                }
                                return result;
                            }
                            /**
                             * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                             * Returns 0 if given 0.
                             */
                            function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                                unchecked {
                                    uint256 result = log10(value);
                                    return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
                                }
                            }
                            /**
                             * @dev Return the log in base 256, rounded down, of a positive value.
                             * Returns 0 if given 0.
                             *
                             * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
                             */
                            function log256(uint256 value) internal pure returns (uint256) {
                                uint256 result = 0;
                                unchecked {
                                    if (value >> 128 > 0) {
                                        value >>= 128;
                                        result += 16;
                                    }
                                    if (value >> 64 > 0) {
                                        value >>= 64;
                                        result += 8;
                                    }
                                    if (value >> 32 > 0) {
                                        value >>= 32;
                                        result += 4;
                                    }
                                    if (value >> 16 > 0) {
                                        value >>= 16;
                                        result += 2;
                                    }
                                    if (value >> 8 > 0) {
                                        result += 1;
                                    }
                                }
                                return result;
                            }
                            /**
                             * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
                             * Returns 0 if given 0.
                             */
                            function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                                unchecked {
                                    uint256 result = log256(value);
                                    return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
                                }
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
                        pragma solidity ^0.8.0;
                        /**
                         * @dev Standard signed math utilities missing in the Solidity language.
                         */
                        library SignedMathUpgradeable {
                            /**
                             * @dev Returns the largest of two signed numbers.
                             */
                            function max(int256 a, int256 b) internal pure returns (int256) {
                                return a > b ? a : b;
                            }
                            /**
                             * @dev Returns the smallest of two signed numbers.
                             */
                            function min(int256 a, int256 b) internal pure returns (int256) {
                                return a < b ? a : b;
                            }
                            /**
                             * @dev Returns the average of two signed numbers without overflow.
                             * The result is rounded towards zero.
                             */
                            function average(int256 a, int256 b) internal pure returns (int256) {
                                // Formula from the book "Hacker's Delight"
                                int256 x = (a & b) + ((a ^ b) >> 1);
                                return x + (int256(uint256(x) >> 255) & (a ^ b));
                            }
                            /**
                             * @dev Returns the absolute unsigned value of a signed value.
                             */
                            function abs(int256 n) internal pure returns (uint256) {
                                unchecked {
                                    // must be unchecked in order to support `n = type(int256).min`
                                    return uint256(n >= 0 ? n : -n);
                                }
                            }
                        }
                        

                        File 11 of 11: TransferProxy
                        pragma solidity ^0.5.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);
                        }
                        
                        /**
                         * @dev Required interface of an ERC721 compliant contract.
                         */
                        contract IERC721 is IERC165 {
                            event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                            event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
                            event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
                        
                            /**
                             * @dev Returns the number of NFTs in `owner`'s account.
                             */
                            function balanceOf(address owner) public view returns (uint256 balance);
                        
                            /**
                             * @dev Returns the owner of the NFT specified by `tokenId`.
                             */
                            function ownerOf(uint256 tokenId) public view returns (address owner);
                        
                            /**
                             * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
                             * another (`to`).
                             *
                             *
                             *
                             * Requirements:
                             * - `from`, `to` cannot be zero.
                             * - `tokenId` must be owned by `from`.
                             * - `tokenId` must be owned by `from`.
                             * - If the caller is not `from`, it must be have been allowed to move this
                             * NFT by either {approve} or {setApprovalForAll}.
                             */
                            function safeTransferFrom(address from, address to, uint256 tokenId) public;
                            /**
                             * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
                             * another (`to`).
                             *
                             * Requirements:
                             * - If the caller is not `from`, it must be approved to move this NFT by
                             * either {approve} or {setApprovalForAll}.
                             */
                            function transferFrom(address from, address to, uint256 tokenId) public;
                            function approve(address to, uint256 tokenId) public;
                            function getApproved(uint256 tokenId) public view returns (address operator);
                        
                            function setApprovalForAll(address operator, bool _approved) public;
                            function isApprovedForAll(address owner, address operator) public view returns (bool);
                        
                        
                            function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
                        }
                        
                        /*
                         * @dev Provides information about the current execution context, including the
                         * sender of the transaction and its data. While these are generally available
                         * via msg.sender and msg.data, they should not be accessed in such a direct
                         * manner, since when dealing with GSN meta-transactions the account sending and
                         * paying for execution may not be the actual sender (as far as an application
                         * is concerned).
                         *
                         * This contract is only required for intermediate, library-like contracts.
                         */
                        contract Context {
                            // Empty internal constructor, to prevent people from mistakenly deploying
                            // an instance of this contract, which should be used via inheritance.
                            constructor () internal { }
                            // solhint-disable-previous-line no-empty-blocks
                        
                            function _msgSender() internal view returns (address payable) {
                                return msg.sender;
                            }
                        
                            function _msgData() internal view returns (bytes memory) {
                                this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                                return msg.data;
                            }
                        }
                        
                        /**
                         * @title Roles
                         * @dev Library for managing addresses assigned to a Role.
                         */
                        library Roles {
                            struct Role {
                                mapping (address => bool) bearer;
                            }
                        
                            /**
                             * @dev Give an account access to this role.
                             */
                            function add(Role storage role, address account) internal {
                                require(!has(role, account), "Roles: account already has role");
                                role.bearer[account] = true;
                            }
                        
                            /**
                             * @dev Remove an account's access to this role.
                             */
                            function remove(Role storage role, address account) internal {
                                require(has(role, account), "Roles: account does not have role");
                                role.bearer[account] = false;
                            }
                        
                            /**
                             * @dev Check if an account has this role.
                             * @return bool
                             */
                            function has(Role storage role, address account) internal view returns (bool) {
                                require(account != address(0), "Roles: account is the zero address");
                                return role.bearer[account];
                            }
                        }
                        
                        contract OperatorRole is Context {
                            using Roles for Roles.Role;
                        
                            event OperatorAdded(address indexed account);
                            event OperatorRemoved(address indexed account);
                        
                            Roles.Role private _operators;
                        
                            constructor () internal {
                        
                            }
                        
                            modifier onlyOperator() {
                                require(isOperator(_msgSender()), "OperatorRole: caller does not have the Operator role");
                                _;
                            }
                        
                            function isOperator(address account) public view returns (bool) {
                                return _operators.has(account);
                            }
                        
                            function _addOperator(address account) internal {
                                _operators.add(account);
                                emit OperatorAdded(account);
                            }
                        
                            function _removeOperator(address account) internal {
                                _operators.remove(account);
                                emit OperatorRemoved(account);
                            }
                        }
                        
                        /**
                         * @dev Contract module which provides a basic access control mechanism, where
                         * there is an account (an owner) that can be granted exclusive access to
                         * specific functions.
                         *
                         * This module is used through inheritance. It will make available the modifier
                         * `onlyOwner`, which can be applied to your functions to restrict their use to
                         * the owner.
                         */
                        contract Ownable is Context {
                            address private _owner;
                        
                            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                        
                            /**
                             * @dev Initializes the contract setting the deployer as the initial owner.
                             */
                            constructor () internal {
                                address msgSender = _msgSender();
                                _owner = msgSender;
                                emit OwnershipTransferred(address(0), msgSender);
                            }
                        
                            /**
                             * @dev Returns the address of the current owner.
                             */
                            function owner() public view returns (address) {
                                return _owner;
                            }
                        
                            /**
                             * @dev Throws if called by any account other than the owner.
                             */
                            modifier onlyOwner() {
                                require(isOwner(), "Ownable: caller is not the owner");
                                _;
                            }
                        
                            /**
                             * @dev Returns true if the caller is the current owner.
                             */
                            function isOwner() public view returns (bool) {
                                return _msgSender() == _owner;
                            }
                        
                            /**
                             * @dev Leaves the contract without owner. It will not be possible to call
                             * `onlyOwner` functions anymore. Can only be called by the current owner.
                             *
                             * NOTE: Renouncing ownership will leave the contract without an owner,
                             * thereby removing any functionality that is only available to the owner.
                             */
                            function renounceOwnership() public onlyOwner {
                                emit OwnershipTransferred(_owner, address(0));
                                _owner = address(0);
                            }
                        
                            /**
                             * @dev Transfers ownership of the contract to a new account (`newOwner`).
                             * Can only be called by the current owner.
                             */
                            function transferOwnership(address newOwner) public onlyOwner {
                                _transferOwnership(newOwner);
                            }
                        
                            /**
                             * @dev Transfers ownership of the contract to a new account (`newOwner`).
                             */
                            function _transferOwnership(address newOwner) internal {
                                require(newOwner != address(0), "Ownable: new owner is the zero address");
                                emit OwnershipTransferred(_owner, newOwner);
                                _owner = newOwner;
                            }
                        }
                        
                        contract OwnableOperatorRole is Ownable, OperatorRole {
                            function addOperator(address account) external onlyOwner {
                                _addOperator(account);
                            }
                        
                            function removeOperator(address account) external onlyOwner {
                                _removeOperator(account);
                            }
                        }
                        
                        /**
                            @title ERC-1155 Multi Token Standard
                            @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md
                            Note: The ERC-165 identifier for this interface is 0xd9b67a26.
                         */
                        contract IERC1155 is IERC165 {
                            /**
                                @dev Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard).
                                The `_operator` argument MUST be msg.sender.
                                The `_from` argument MUST be the address of the holder whose balance is decreased.
                                The `_to` argument MUST be the address of the recipient whose balance is increased.
                                The `_id` argument MUST be the token type being transferred.
                                The `_value` argument MUST be the number of tokens the holder balance is decreased by and match what the recipient balance is increased by.
                                When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address).
                                When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address).
                            */
                            event TransferSingle(address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value);
                        
                            /**
                                @dev Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard).
                                The `_operator` argument MUST be msg.sender.
                                The `_from` argument MUST be the address of the holder whose balance is decreased.
                                The `_to` argument MUST be the address of the recipient whose balance is increased.
                                The `_ids` argument MUST be the list of tokens being transferred.
                                The `_values` argument MUST be the list of number of tokens (matching the list and order of tokens specified in _ids) the holder balance is decreased by and match what the recipient balance is increased by.
                                When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address).
                                When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address).
                            */
                            event TransferBatch(address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values);
                        
                            /**
                                @dev MUST emit when approval for a second party/operator address to manage all tokens for an owner address is enabled or disabled (absense of an event assumes disabled).
                            */
                            event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
                        
                            /**
                                @dev MUST emit when the URI is updated for a token ID.
                                URIs are defined in RFC 3986.
                                The URI MUST point a JSON file that conforms to the "ERC-1155 Metadata URI JSON Schema".
                            */
                            event URI(string _value, uint256 indexed _id);
                        
                            /**
                                @notice Transfers `_value` amount of an `_id` from the `_from` address to the `_to` address specified (with safety call).
                                @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard).
                                MUST revert if `_to` is the zero address.
                                MUST revert if balance of holder for token `_id` is lower than the `_value` sent.
                                MUST revert on any other error.
                                MUST emit the `TransferSingle` event to reflect the balance change (see "Safe Transfer Rules" section of the standard).
                                After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call `onERC1155Received` on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard).
                                @param _from    Source address
                                @param _to      Target address
                                @param _id      ID of the token type
                                @param _value   Transfer amount
                                @param _data    Additional data with no specified format, MUST be sent unaltered in call to `onERC1155Received` on `_to`
                            */
                            function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;
                        
                            /**
                                @notice Transfers `_values` amount(s) of `_ids` from the `_from` address to the `_to` address specified (with safety call).
                                @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard).
                                MUST revert if `_to` is the zero address.
                                MUST revert if length of `_ids` is not the same as length of `_values`.
                                MUST revert if any of the balance(s) of the holder(s) for token(s) in `_ids` is lower than the respective amount(s) in `_values` sent to the recipient.
                                MUST revert on any other error.
                                MUST emit `TransferSingle` or `TransferBatch` event(s) such that all the balance changes are reflected (see "Safe Transfer Rules" section of the standard).
                                Balance changes and events MUST follow the ordering of the arrays (_ids[0]/_values[0] before _ids[1]/_values[1], etc).
                                After the above conditions for the transfer(s) in the batch are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call the relevant `ERC1155TokenReceiver` hook(s) on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard).
                                @param _from    Source address
                                @param _to      Target address
                                @param _ids     IDs of each token type (order and length must match _values array)
                                @param _values  Transfer amounts per token type (order and length must match _ids array)
                                @param _data    Additional data with no specified format, MUST be sent unaltered in call to the `ERC1155TokenReceiver` hook(s) on `_to`
                            */
                            function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external;
                        
                            /**
                                @notice Get the balance of an account's Tokens.
                                @param _owner  The address of the token holder
                                @param _id     ID of the Token
                                @return        The _owner's balance of the Token type requested
                             */
                            function balanceOf(address _owner, uint256 _id) external view returns (uint256);
                        
                            /**
                                @notice Get the balance of multiple account/token pairs
                                @param _owners The addresses of the token holders
                                @param _ids    ID of the Tokens
                                @return        The _owner's balance of the Token types requested (i.e. balance for each (owner, id) pair)
                             */
                            function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) external view returns (uint256[] memory);
                        
                            /**
                                @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens.
                                @dev MUST emit the ApprovalForAll event on success.
                                @param _operator  Address to add to the set of authorized operators
                                @param _approved  True if the operator is approved, false to revoke approval
                            */
                            function setApprovalForAll(address _operator, bool _approved) external;
                        
                            /**
                                @notice Queries the approval status of an operator for a given owner.
                                @param _owner     The owner of the Tokens
                                @param _operator  Address of authorized operator
                                @return           True if the operator is approved, false if not
                            */
                            function isApprovedForAll(address _owner, address _operator) external view returns (bool);
                        }
                        
                        contract TransferProxy is OwnableOperatorRole {
                        
                            function erc721safeTransferFrom(IERC721 token, address from, address to, uint256 tokenId) external onlyOperator {
                                token.safeTransferFrom(from, to, tokenId);
                            }
                        
                            function erc1155safeTransferFrom(IERC1155 token, address from, address to, uint256 id, uint256 value, bytes calldata data) external onlyOperator {
                                token.safeTransferFrom(from, to, id, value, data);
                            }
                        }