ETH Price: $3,117.28 (+0.27%)
Gas: 3 Gwei

Transaction Decoder

Block:
19528473 at Mar-27-2024 10:24:47 PM +UTC
Transaction Fee:
0.001940990190798768 ETH $6.05
Gas Used:
52,012 Gas / 37.318122564 Gwei

Emitted Events:

420 BeaconProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000781b192f48706310082268a4c037078f2e8b9b0, 0x000000000000000000000000a9d93a5cca9c98512c8c56547866b1db09090326, 00000000000000000000000000000000000000000000000000005af3107a4000 )

Account State Difference:

  Address   Before After State Difference Code
0x0781B192...8F2e8B9B0
0.02188072644259084 Eth
Nonce: 959
0.019939736251792072 Eth
Nonce: 960
0.001940990190798768
0x269616D5...12D11232A
3.312401768626623978 Eth3.312402020884823978 Eth0.0000002522582

Execution Trace

BeaconProxy.a9059cbb( )
  • AdminUpgradeabilityProxy.STATICCALL( )
    • NFTXVaultFactoryUpgradeable.DELEGATECALL( )
    • NFTXVaultUpgradeable.transfer( recipient=0xA9d93A5cCa9c98512C8C56547866b1db09090326, amount=100000000000000 ) => ( True )
      File 1 of 4: BeaconProxy
      // Sources flattened with hardhat v2.0.11 https://hardhat.org
      
      // File contracts/solidity/proxy/IBeacon.sol
      
      // 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 childImplementation() external view returns (address);
          function upgradeChildTo(address newImplementation) external;
      }
      
      
      // File contracts/solidity/proxy/Proxy.sol
      
      
      
      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 {
          }
      }
      
      
      // File contracts/solidity/util/Address.sol
      
      
      
      pragma solidity ^0.8.0;
      
      /**
       * @dev Collection of functions related to the address type
       */
      library Address {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies on extcodesize, which returns 0 for contracts in
              // construction, since the code is only stored at the end of the
              // constructor execution.
      
              uint256 size;
              // 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);
                  }
              }
          }
      }
      
      
      // File contracts/solidity/proxy/BeaconProxy.sol
      
      
      
      pragma solidity ^0.8.0;
      
      
      
      /**
       * @dev This contract implements a proxy that gets the implementation address for each call from a {UpgradeableBeacon}.
       *
       * The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't
       * conflict with the storage layout of the implementation behind the proxy.
       *
       * _Available since v3.4._
       */
      contract BeaconProxy is Proxy {
          /**
           * @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 private constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
      
          /**
           * @dev Initializes the proxy with `beacon`.
           *
           * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This
           * will typically be an encoded function call, and allows initializating the storage of the proxy like a Solidity
           * constructor.
           *
           * Requirements:
           *
           * - `beacon` must be a contract with the interface {IBeacon}.
           */
          constructor(address beacon, bytes memory data) payable {
              assert(_BEACON_SLOT == bytes32(uint256(keccak256("eip1967.proxy.beacon")) - 1));
              _setBeacon(beacon, data);
          }
      
          /**
           * @dev Returns the current beacon address.
           */
          function _beacon() internal view virtual returns (address beacon) {
              bytes32 slot = _BEACON_SLOT;
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  beacon := sload(slot)
              }
          }
      
          /**
           * @dev Returns the current implementation address of the associated beacon.
           */
          function _implementation() internal view virtual override returns (address) {
              return IBeacon(_beacon()).childImplementation();
          }
      
          /**
           * @dev Changes the proxy to use a new beacon.
           *
           * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon.
           *
           * Requirements:
           *
           * - `beacon` must be a contract.
           * - The implementation returned by `beacon` must be a contract.
           */
          function _setBeacon(address beacon, bytes memory data) internal virtual {
              require(
                  Address.isContract(beacon),
                  "BeaconProxy: beacon is not a contract"
              );
              require(
                  Address.isContract(IBeacon(beacon).childImplementation()),
                  "BeaconProxy: beacon implementation is not a contract"
              );
              bytes32 slot = _BEACON_SLOT;
      
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  sstore(slot, beacon)
              }
      
              if (data.length > 0) {
                  Address.functionDelegateCall(_implementation(), data, "BeaconProxy: function call failed");
              }
          }
      }

      File 2 of 4: AdminUpgradeabilityProxy
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.6.0;
      import './UpgradeabilityProxy.sol';
      /**
       * @title AdminUpgradeabilityProxy
       * @dev This contract combines an upgradeability proxy with an authorization
       * mechanism for administrative tasks.
       * All external functions in this contract must be guarded by the
       * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
       * feature proposal that would enable this to be done automatically.
       */
      contract AdminUpgradeabilityProxy is UpgradeabilityProxy {
        /**
         * Contract constructor.
         * @param _logic address of the initial implementation.
         * @param _admin Address of the proxy administrator.
         * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
         */
        constructor(address _logic, address _admin, bytes memory _data) UpgradeabilityProxy(_logic, _data) public payable {
          assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1));
          _setAdmin(_admin);
        }
        /**
         * @dev Emitted when the administration has been transferred.
         * @param previousAdmin Address of the previous admin.
         * @param newAdmin Address of the new admin.
         */
        event AdminChanged(address previousAdmin, address newAdmin);
        /**
         * @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 Modifier to check whether the `msg.sender` is the admin.
         * If it is, it will run the function. Otherwise, it will delegate the call
         * to the implementation.
         */
        modifier ifAdmin() {
          if (msg.sender == _admin()) {
            _;
          } else {
            _fallback();
          }
        }
        /**
         * @return The address of the proxy admin.
         */
        function admin() external ifAdmin returns (address) {
          return _admin();
        }
        /**
         * @return The address of the implementation.
         */
        function implementation() external ifAdmin returns (address) {
          return _implementation();
        }
        /**
         * @dev Changes the admin of the proxy.
         * Only the current admin can call this function.
         * @param newAdmin Address to transfer proxy administration to.
         */
        function changeAdmin(address newAdmin) external ifAdmin {
          require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
          emit AdminChanged(_admin(), newAdmin);
          _setAdmin(newAdmin);
        }
        /**
         * @dev Upgrade the backing implementation of the proxy.
         * Only the admin can call this function.
         * @param newImplementation Address of the new implementation.
         */
        function upgradeTo(address newImplementation) external ifAdmin {
          _upgradeTo(newImplementation);
        }
        /**
         * @dev Upgrade the backing implementation of the proxy and call a function
         * on the new implementation.
         * This is useful to initialize the proxied contract.
         * @param newImplementation Address of the new implementation.
         * @param data Data to send as msg.data in the low level call.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         */
        function upgradeToAndCall(address newImplementation, bytes calldata data) payable external ifAdmin {
          _upgradeTo(newImplementation);
          (bool success,) = newImplementation.delegatecall(data);
          require(success);
        }
        /**
         * @return adm The admin slot.
         */
        function _admin() internal view returns (address adm) {
          bytes32 slot = ADMIN_SLOT;
          assembly {
            adm := sload(slot)
          }
        }
        /**
         * @dev Sets the address of the proxy admin.
         * @param newAdmin Address of the new proxy admin.
         */
        function _setAdmin(address newAdmin) internal {
          bytes32 slot = ADMIN_SLOT;
          assembly {
            sstore(slot, newAdmin)
          }
        }
        /**
         * @dev Only fall back when the sender is not the admin.
         */
        function _willFallback() internal override virtual {
          require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
          super._willFallback();
        }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.6.0;
      import './Proxy.sol';
      import '@openzeppelin/contracts/utils/Address.sol';
      /**
       * @title UpgradeabilityProxy
       * @dev This contract implements a proxy that allows to change the
       * implementation address to which it will delegate.
       * Such a change is called an implementation upgrade.
       */
      contract UpgradeabilityProxy is Proxy {
        /**
         * @dev Contract constructor.
         * @param _logic Address of the initial implementation.
         * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
         */
        constructor(address _logic, bytes memory _data) public payable {
          assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
          _setImplementation(_logic);
          if(_data.length > 0) {
            (bool success,) = _logic.delegatecall(_data);
            require(success);
          }
        }  
        /**
         * @dev Emitted when the implementation is upgraded.
         * @param implementation Address of the new implementation.
         */
        event Upgraded(address indexed implementation);
        /**
         * @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 Returns the current implementation.
         * @return impl Address of the current implementation
         */
        function _implementation() internal override view returns (address impl) {
          bytes32 slot = IMPLEMENTATION_SLOT;
          assembly {
            impl := sload(slot)
          }
        }
        /**
         * @dev Upgrades the proxy to a new implementation.
         * @param newImplementation Address of the new implementation.
         */
        function _upgradeTo(address newImplementation) internal {
          _setImplementation(newImplementation);
          emit Upgraded(newImplementation);
        }
        /**
         * @dev Sets the implementation address of the proxy.
         * @param newImplementation Address of the new implementation.
         */
        function _setImplementation(address newImplementation) internal {
          require(Address.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
          bytes32 slot = IMPLEMENTATION_SLOT;
          assembly {
            sstore(slot, newImplementation)
          }
        }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.6.0;
      /**
       * @title Proxy
       * @dev Implements delegation of calls to other contracts, with proper
       * forwarding of return values and bubbling of failures.
       * It defines a fallback function that delegates all calls to the address
       * returned by the abstract _implementation() internal function.
       */
      abstract contract Proxy {
        /**
         * @dev Fallback function.
         * Implemented entirely in `_fallback`.
         */
        fallback () payable external {
          _fallback();
        }
        /**
         * @dev Receive function.
         * Implemented entirely in `_fallback`.
         */
        receive () payable external {
          _fallback();
        }
        /**
         * @return The Address of the implementation.
         */
        function _implementation() internal virtual view returns (address);
        /**
         * @dev Delegates execution to an implementation contract.
         * This is a low level function that doesn't return to its internal call site.
         * It will return to the external caller whatever the implementation returns.
         * @param implementation Address to delegate.
         */
        function _delegate(address implementation) internal {
          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 Function that is run as the first thing in the fallback function.
         * Can be redefined in derived contracts to add functionality.
         * Redefinitions must call super._willFallback().
         */
        function _willFallback() internal virtual {
        }
        /**
         * @dev fallback implementation.
         * Extracted to enable manual triggering.
         */
        function _fallback() internal {
          _willFallback();
          _delegate(_implementation());
        }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.6.2 <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);
          }
          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);
                  }
              }
          }
      }
      

      File 3 of 4: NFTXVaultFactoryUpgradeable
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "./util/PausableUpgradeable.sol";
      import "./proxy/UpgradeableBeacon.sol";
      import "./proxy/BeaconProxy.sol";
      import "./interface/INFTXVaultFactory.sol";
      import "./interface/INFTXFeeDistributor.sol";
      import "./NFTXVaultUpgradeable.sol";
      // Authors: @0xKiwi_ and @alexgausman.
      contract NFTXVaultFactoryUpgradeable is
          PausableUpgradeable,
          UpgradeableBeacon,
          INFTXVaultFactory
      {
          uint256 private NOT_USED1; // Removed, no longer needed.
          address public override zapContract; // No longer needed, but keeping for compatibility.
          address public override feeDistributor;
          address public override eligibilityManager;
          mapping(uint256 => address) private NOT_USED3; // Removed, no longer needed.
          mapping(address => address[]) _vaultsForAsset;
          address[] internal vaults;
          // v1.0.1
          mapping(address => bool) public override excludedFromFees;
          // v1.0.2
          struct VaultFees {
              bool active;
              uint64 mintFee;
              uint64 randomRedeemFee;
              uint64 targetRedeemFee;
              uint64 randomSwapFee;
              uint64 targetSwapFee;
          }
          mapping(uint256 => VaultFees) private _vaultFees;
          uint64 public override factoryMintFee;
          uint64 public override factoryRandomRedeemFee;
          uint64 public override factoryTargetRedeemFee;
          uint64 public override factoryRandomSwapFee;
          uint64 public override factoryTargetSwapFee;
          // v1.0.3
          mapping(address => bool) public override zapContracts;
          function __NFTXVaultFactory_init(
              address _vaultImpl,
              address _feeDistributor
          ) public override initializer {
              __Pausable_init();
              // We use a beacon proxy so that every child contract follows the same implementation code.
              __UpgradeableBeacon__init(_vaultImpl);
              setFeeDistributor(_feeDistributor);
              setFactoryFees(0.1 ether, 0.05 ether, 0.1 ether, 0.05 ether, 0.1 ether);
          }
          function createVault(
              string memory name,
              string memory symbol,
              address _assetAddress,
              bool is1155,
              bool allowAllItems
          ) external virtual override returns (uint256) {
              onlyOwnerIfPaused(0);
              require(feeDistributor != address(0), "NFTX: Fee receiver unset");
              require(
                  childImplementation() != address(0),
                  "NFTX: Vault implementation unset"
              );
              address vaultAddr = deployVault(
                  name,
                  symbol,
                  _assetAddress,
                  is1155,
                  allowAllItems
              );
              uint256 _vaultId = vaults.length;
              _vaultsForAsset[_assetAddress].push(vaultAddr);
              vaults.push(vaultAddr);
              INFTXFeeDistributor(feeDistributor).initializeVaultReceivers(_vaultId);
              emit NewVault(_vaultId, vaultAddr, _assetAddress);
              return _vaultId;
          }
          function setFactoryFees(
              uint256 mintFee,
              uint256 randomRedeemFee,
              uint256 targetRedeemFee,
              uint256 randomSwapFee,
              uint256 targetSwapFee
          ) public virtual override onlyOwner {
              require(mintFee <= 0.5 ether, "Cannot > 0.5 ether");
              require(randomRedeemFee <= 0.5 ether, "Cannot > 0.5 ether");
              require(targetRedeemFee <= 0.5 ether, "Cannot > 0.5 ether");
              require(randomSwapFee <= 0.5 ether, "Cannot > 0.5 ether");
              require(targetSwapFee <= 0.5 ether, "Cannot > 0.5 ether");
              factoryMintFee = uint64(mintFee);
              factoryRandomRedeemFee = uint64(randomRedeemFee);
              factoryTargetRedeemFee = uint64(targetRedeemFee);
              factoryRandomSwapFee = uint64(randomSwapFee);
              factoryTargetSwapFee = uint64(targetSwapFee);
              emit UpdateFactoryFees(
                  mintFee,
                  randomRedeemFee,
                  targetRedeemFee,
                  randomSwapFee,
                  targetSwapFee
              );
          }
          function setVaultFees(
              uint256 vaultId,
              uint256 mintFee,
              uint256 randomRedeemFee,
              uint256 targetRedeemFee,
              uint256 randomSwapFee,
              uint256 targetSwapFee
          ) public virtual override {
              if (msg.sender != owner()) {
                  address vaultAddr = vaults[vaultId];
                  require(msg.sender == vaultAddr, "Not from vault");
              }
              require(mintFee <= 0.5 ether, "Cannot > 0.5 ether");
              require(randomRedeemFee <= 0.5 ether, "Cannot > 0.5 ether");
              require(targetRedeemFee <= 0.5 ether, "Cannot > 0.5 ether");
              require(randomSwapFee <= 0.5 ether, "Cannot > 0.5 ether");
              require(targetSwapFee <= 0.5 ether, "Cannot > 0.5 ether");
              _vaultFees[vaultId] = VaultFees(
                  true,
                  uint64(mintFee),
                  uint64(randomRedeemFee),
                  uint64(targetRedeemFee),
                  uint64(randomSwapFee),
                  uint64(targetSwapFee)
              );
              emit UpdateVaultFees(
                  vaultId,
                  mintFee,
                  randomRedeemFee,
                  targetRedeemFee,
                  randomSwapFee,
                  targetSwapFee
              );
          }
          function disableVaultFees(uint256 vaultId) public virtual override {
              if (msg.sender != owner()) {
                  address vaultAddr = vaults[vaultId];
                  require(msg.sender == vaultAddr, "Not vault");
              }
              delete _vaultFees[vaultId];
              emit DisableVaultFees(vaultId);
          }
          function setFeeDistributor(address _feeDistributor)
              public
              virtual
              override
              onlyOwner
          {
              require(_feeDistributor != address(0));
              emit NewFeeDistributor(feeDistributor, _feeDistributor);
              feeDistributor = _feeDistributor;
          }
          function setZapContract(address _zapContract, bool _excluded)
              public
              virtual
              override
              onlyOwner
          {
              emit UpdatedZapContract(_zapContract, _excluded);
              zapContracts[_zapContract] = _excluded;
          }
          function setFeeExclusion(address _excludedAddr, bool excluded)
              public
              virtual
              override
              onlyOwner
          {
              emit FeeExclusion(_excludedAddr, excluded);
              excludedFromFees[_excludedAddr] = excluded;
          }
          function setEligibilityManager(address _eligibilityManager)
              external
              virtual
              override
              onlyOwner
          {
              emit NewEligibilityManager(eligibilityManager, _eligibilityManager);
              eligibilityManager = _eligibilityManager;
          }
          function vaultFees(uint256 vaultId)
              external
              view
              virtual
              override
              returns (
                  uint256,
                  uint256,
                  uint256,
                  uint256,
                  uint256
              )
          {
              VaultFees memory fees = _vaultFees[vaultId];
              if (fees.active) {
                  return (
                      uint256(fees.mintFee),
                      uint256(fees.randomRedeemFee),
                      uint256(fees.targetRedeemFee),
                      uint256(fees.randomSwapFee),
                      uint256(fees.targetSwapFee)
                  );
              }
              return (
                  uint256(factoryMintFee),
                  uint256(factoryRandomRedeemFee),
                  uint256(factoryTargetRedeemFee),
                  uint256(factoryRandomSwapFee),
                  uint256(factoryTargetSwapFee)
              );
          }
          function isLocked(uint256 lockId)
              external
              view
              virtual
              override
              returns (bool)
          {
              return isPaused[lockId];
          }
          function vaultsForAsset(address assetAddress)
              external
              view
              virtual
              override
              returns (address[] memory)
          {
              return _vaultsForAsset[assetAddress];
          }
          function vault(uint256 vaultId)
              external
              view
              virtual
              override
              returns (address)
          {
              return vaults[vaultId];
          }
          function allVaults()
              external
              view
              virtual
              override
              returns (address[] memory)
          {
              return vaults;
          }
          function numVaults() external view virtual override returns (uint256) {
              return vaults.length;
          }
          function deployVault(
              string memory name,
              string memory symbol,
              address _assetAddress,
              bool is1155,
              bool allowAllItems
          ) internal returns (address) {
              address newBeaconProxy = address(new BeaconProxy(address(this), ""));
              NFTXVaultUpgradeable(newBeaconProxy).__NFTXVault_init(
                  name,
                  symbol,
                  _assetAddress,
                  is1155,
                  allowAllItems
              );
              // Manager for configuration.
              NFTXVaultUpgradeable(newBeaconProxy).setManager(msg.sender);
              // Owner for administrative functions.
              NFTXVaultUpgradeable(newBeaconProxy).transferOwnership(owner());
              return newBeaconProxy;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "./util/OwnableUpgradeable.sol";
      import "./util/ReentrancyGuardUpgradeable.sol";
      import "./util/EnumerableSetUpgradeable.sol";
      import "./token/ERC20FlashMintUpgradeable.sol";
      import "./token/ERC721SafeHolderUpgradeable.sol";
      import "./token/ERC1155SafeHolderUpgradeable.sol";
      import "./token/IERC1155Upgradeable.sol";
      import "./token/IERC721Upgradeable.sol";
      import "./interface/INFTXVault.sol";
      import "./interface/INFTXEligibilityManager.sol";
      import "./interface/INFTXFeeDistributor.sol";
      // Authors: @0xKiwi_ and @alexgausman.
      contract NFTXVaultUpgradeable is
          OwnableUpgradeable,
          ERC20FlashMintUpgradeable,
          ReentrancyGuardUpgradeable,
          ERC721SafeHolderUpgradeable,
          ERC1155SafeHolderUpgradeable,
          INFTXVault
      {
          using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;
          uint256 constant base = 10**18;
          uint256 public override vaultId;
          address public override manager;
          address public override assetAddress;
          INFTXVaultFactory public override vaultFactory;
          INFTXEligibility public override eligibilityStorage;
          uint256 randNonce;
          uint256 private UNUSED_FEE1;
          uint256 private UNUSED_FEE2;
          uint256 private UNUSED_FEE3;
          bool public override is1155;
          bool public override allowAllItems;
          bool public override enableMint;
          bool public override enableRandomRedeem;
          bool public override enableTargetRedeem;
          EnumerableSetUpgradeable.UintSet holdings;
          mapping(uint256 => uint256) quantity1155;
          bool public override enableRandomSwap;
          bool public override enableTargetSwap;
          function __NFTXVault_init(
              string memory _name,
              string memory _symbol,
              address _assetAddress,
              bool _is1155,
              bool _allowAllItems
          ) public virtual override initializer {
              __Ownable_init();
              __ERC20_init(_name, _symbol);
              require(_assetAddress != address(0), "Asset != address(0)");
              assetAddress = _assetAddress;
              vaultFactory = INFTXVaultFactory(msg.sender);
              vaultId = vaultFactory.numVaults();
              is1155 = _is1155;
              allowAllItems = _allowAllItems;
              emit VaultInit(vaultId, _assetAddress, _is1155, _allowAllItems);
              setVaultFeatures(
                  true, /*enableMint*/
                  true, /*enableRandomRedeem*/
                  true, /*enableTargetRedeem*/
                  true, /*enableRandomSwap*/
                  true /*enableTargetSwap*/
              );
          }
          function finalizeVault() external virtual override {
              setManager(address(0));
          }
          // Added in v1.0.3.
          function setVaultMetadata(string calldata name_, string calldata symbol_)
              external
              virtual
              override
          {
              onlyPrivileged();
              _setMetadata(name_, symbol_);
          }
          function setVaultFeatures(
              bool _enableMint,
              bool _enableRandomRedeem,
              bool _enableTargetRedeem,
              bool _enableRandomSwap,
              bool _enableTargetSwap
          ) public virtual override {
              onlyPrivileged();
              enableMint = _enableMint;
              enableRandomRedeem = _enableRandomRedeem;
              enableTargetRedeem = _enableTargetRedeem;
              enableRandomSwap = _enableRandomSwap;
              enableTargetSwap = _enableTargetSwap;
              emit EnableMintUpdated(_enableMint);
              emit EnableRandomRedeemUpdated(_enableRandomRedeem);
              emit EnableTargetRedeemUpdated(_enableTargetRedeem);
              emit EnableRandomSwapUpdated(_enableRandomSwap);
              emit EnableTargetSwapUpdated(_enableTargetSwap);
          }
          function setFees(
              uint256 _mintFee,
              uint256 _randomRedeemFee,
              uint256 _targetRedeemFee,
              uint256 _randomSwapFee,
              uint256 _targetSwapFee
          ) public virtual override {
              onlyPrivileged();
              vaultFactory.setVaultFees(
                  vaultId,
                  _mintFee,
                  _randomRedeemFee,
                  _targetRedeemFee,
                  _randomSwapFee,
                  _targetSwapFee
              );
          }
          function disableVaultFees() public virtual override {
              onlyPrivileged();
              vaultFactory.disableVaultFees(vaultId);
          }
          // This function allows for an easy setup of any eligibility module contract from the EligibilityManager.
          // It takes in ABI encoded parameters for the desired module. This is to make sure they can all follow
          // a similar interface.
          function deployEligibilityStorage(
              uint256 moduleIndex,
              bytes calldata initData
          ) external virtual override returns (address) {
              onlyPrivileged();
              require(
                  address(eligibilityStorage) == address(0),
                  "NFTXVault: eligibility already set"
              );
              INFTXEligibilityManager eligManager = INFTXEligibilityManager(
                  vaultFactory.eligibilityManager()
              );
              address _eligibility = eligManager.deployEligibility(
                  moduleIndex,
                  initData
              );
              eligibilityStorage = INFTXEligibility(_eligibility);
              // Toggle this to let the contract know to check eligibility now.
              allowAllItems = false;
              emit EligibilityDeployed(moduleIndex, _eligibility);
              return _eligibility;
          }
          // // This function allows for the manager to set their own arbitrary eligibility contract.
          // // Once eligiblity is set, it cannot be unset or changed.
          // Disabled for launch.
          // function setEligibilityStorage(address _newEligibility) public virtual {
          //     onlyPrivileged();
          //     require(
          //         address(eligibilityStorage) == address(0),
          //         "NFTXVault: eligibility already set"
          //     );
          //     eligibilityStorage = INFTXEligibility(_newEligibility);
          //     // Toggle this to let the contract know to check eligibility now.
          //     allowAllItems = false;
          //     emit CustomEligibilityDeployed(address(_newEligibility));
          // }
          // The manager has control over options like fees and features
          function setManager(address _manager) public virtual override {
              onlyPrivileged();
              manager = _manager;
              emit ManagerSet(_manager);
          }
          function mint(
              uint256[] calldata tokenIds,
              uint256[] calldata amounts /* ignored for ERC721 vaults */
          ) external virtual override returns (uint256) {
              return mintTo(tokenIds, amounts, msg.sender);
          }
          function mintTo(
              uint256[] memory tokenIds,
              uint256[] memory amounts, /* ignored for ERC721 vaults */
              address to
          ) public virtual override nonReentrant returns (uint256) {
              onlyOwnerIfPaused(1);
              require(enableMint, "Minting not enabled");
              // Take the NFTs.
              uint256 count = receiveNFTs(tokenIds, amounts);
              // Mint to the user.
              _mint(to, base * count);
              uint256 totalFee = mintFee() * count;
              _chargeAndDistributeFees(to, totalFee);
              emit Minted(tokenIds, amounts, to);
              return count;
          }
          function redeem(uint256 amount, uint256[] calldata specificIds)
              external
              virtual
              override
              returns (uint256[] memory)
          {
              return redeemTo(amount, specificIds, msg.sender);
          }
          function redeemTo(
              uint256 amount,
              uint256[] memory specificIds,
              address to
          ) public virtual override nonReentrant returns (uint256[] memory) {
              onlyOwnerIfPaused(2);
              require(
                  amount == specificIds.length || enableRandomRedeem,
                  "NFTXVault: Random redeem not enabled"
              );
              require(
                  specificIds.length == 0 || enableTargetRedeem,
                  "NFTXVault: Target redeem not enabled"
              );
              // We burn all from sender and mint to fee receiver to reduce costs.
              _burn(msg.sender, base * amount);
              // Pay the tokens + toll.
              (
                  ,
                  uint256 _randomRedeemFee,
                  uint256 _targetRedeemFee,
                  ,
              ) = vaultFees();
              uint256 totalFee = (_targetRedeemFee * specificIds.length) +
                  (_randomRedeemFee * (amount - specificIds.length));
              _chargeAndDistributeFees(msg.sender, totalFee);
              // Withdraw from vault.
              uint256[] memory redeemedIds = withdrawNFTsTo(amount, specificIds, to);
              emit Redeemed(redeemedIds, specificIds, to);
              return redeemedIds;
          }
          function swap(
              uint256[] calldata tokenIds,
              uint256[] calldata amounts, /* ignored for ERC721 vaults */
              uint256[] calldata specificIds
          ) external virtual override returns (uint256[] memory) {
              return swapTo(tokenIds, amounts, specificIds, msg.sender);
          }
          function swapTo(
              uint256[] memory tokenIds,
              uint256[] memory amounts, /* ignored for ERC721 vaults */
              uint256[] memory specificIds,
              address to
          ) public virtual override nonReentrant returns (uint256[] memory) {
              onlyOwnerIfPaused(3);
              uint256 count;
              if (is1155) {
                  for (uint256 i; i < tokenIds.length; ++i) {
                      uint256 amount = amounts[i];
                      require(amount != 0, "NFTXVault: transferring < 1");
                      count += amount;
                  }
              } else {
                  count = tokenIds.length;
              }
              require(
                  count == specificIds.length || enableRandomSwap,
                  "NFTXVault: Random swap disabled"
              );
              require(
                  specificIds.length == 0 || enableTargetSwap,
                  "NFTXVault: Target swap disabled"
              );
              (, , , uint256 _randomSwapFee, uint256 _targetSwapFee) = vaultFees();
              uint256 totalFee = (_targetSwapFee * specificIds.length) +
                  (_randomSwapFee * (count - specificIds.length));
              _chargeAndDistributeFees(msg.sender, totalFee);
              // Give the NFTs first, so the user wont get the same thing back, just to be nice.
              uint256[] memory ids = withdrawNFTsTo(count, specificIds, to);
              receiveNFTs(tokenIds, amounts);
              emit Swapped(tokenIds, amounts, specificIds, ids, to);
              return ids;
          }
          function flashLoan(
              IERC3156FlashBorrowerUpgradeable receiver,
              address token,
              uint256 amount,
              bytes memory data
          ) public virtual override returns (bool) {
              onlyOwnerIfPaused(4);
              return super.flashLoan(receiver, token, amount, data);
          }
          function mintFee() public view virtual override returns (uint256) {
              (uint256 _mintFee, , , , ) = vaultFactory.vaultFees(vaultId);
              return _mintFee;
          }
          function randomRedeemFee() public view virtual override returns (uint256) {
              (, uint256 _randomRedeemFee, , , ) = vaultFactory.vaultFees(vaultId);
              return _randomRedeemFee;
          }
          function targetRedeemFee() public view virtual override returns (uint256) {
              (, , uint256 _targetRedeemFee, , ) = vaultFactory.vaultFees(vaultId);
              return _targetRedeemFee;
          }
          function randomSwapFee() public view virtual override returns (uint256) {
              (, , , uint256 _randomSwapFee, ) = vaultFactory.vaultFees(vaultId);
              return _randomSwapFee;
          }
          function targetSwapFee() public view virtual override returns (uint256) {
              (, , , , uint256 _targetSwapFee) = vaultFactory.vaultFees(vaultId);
              return _targetSwapFee;
          }
          function vaultFees()
              public
              view
              virtual
              override
              returns (
                  uint256,
                  uint256,
                  uint256,
                  uint256,
                  uint256
              )
          {
              return vaultFactory.vaultFees(vaultId);
          }
          function allValidNFTs(uint256[] memory tokenIds)
              public
              view
              virtual
              override
              returns (bool)
          {
              if (allowAllItems) {
                  return true;
              }
              INFTXEligibility _eligibilityStorage = eligibilityStorage;
              if (address(_eligibilityStorage) == address(0)) {
                  return false;
              }
              return _eligibilityStorage.checkAllEligible(tokenIds);
          }
          function nftIdAt(uint256 holdingsIndex)
              external
              view
              virtual
              override
              returns (uint256)
          {
              return holdings.at(holdingsIndex);
          }
          // Added in v1.0.3.
          function allHoldings()
              external
              view
              virtual
              override
              returns (uint256[] memory)
          {
              uint256 len = holdings.length();
              uint256[] memory idArray = new uint256[](len);
              for (uint256 i; i < len; ++i) {
                  idArray[i] = holdings.at(i);
              }
              return idArray;
          }
          // Added in v1.0.3.
          function totalHoldings() external view virtual override returns (uint256) {
              return holdings.length();
          }
          // Added in v1.0.3.
          function version() external pure returns (string memory) {
              return "v1.0.5";
          }
          // We set a hook to the eligibility module (if it exists) after redeems in case anything needs to be modified.
          function afterRedeemHook(uint256[] memory tokenIds) internal virtual {
              INFTXEligibility _eligibilityStorage = eligibilityStorage;
              if (address(_eligibilityStorage) == address(0)) {
                  return;
              }
              _eligibilityStorage.afterRedeemHook(tokenIds);
          }
          function receiveNFTs(uint256[] memory tokenIds, uint256[] memory amounts)
              internal
              virtual
              returns (uint256)
          {
              require(allValidNFTs(tokenIds), "NFTXVault: not eligible");
              uint256 length = tokenIds.length;
              if (is1155) {
                  // This is technically a check, so placing it before the effect.
                  IERC1155Upgradeable(assetAddress).safeBatchTransferFrom(
                      msg.sender,
                      address(this),
                      tokenIds,
                      amounts,
                      ""
                  );
                  uint256 count;
                  for (uint256 i; i < length; ++i) {
                      uint256 tokenId = tokenIds[i];
                      uint256 amount = amounts[i];
                      require(amount != 0, "NFTXVault: transferring < 1");
                      if (quantity1155[tokenId] == 0) {
                          holdings.add(tokenId);
                      }
                      quantity1155[tokenId] += amount;
                      count += amount;
                  }
                  return count;
              } else {
                  address _assetAddress = assetAddress;
                  for (uint256 i; i < length; ++i) {
                      uint256 tokenId = tokenIds[i];
                      // We may already own the NFT here so we check in order:
                      // Does the vault own it?
                      //   - If so, check if its in holdings list
                      //      - If so, we reject. This means the NFT has already been claimed for.
                      //      - If not, it means we have not yet accounted for this NFT, so we continue.
                      //   -If not, we "pull" it from the msg.sender and add to holdings.
                      transferFromERC721(_assetAddress, tokenId);
                      holdings.add(tokenId);
                  }
                  return length;
              }
          }
          function withdrawNFTsTo(
              uint256 amount,
              uint256[] memory specificIds,
              address to
          ) internal virtual returns (uint256[] memory) {
              bool _is1155 = is1155;
              address _assetAddress = assetAddress;
              uint256[] memory redeemedIds = new uint256[](amount);
              uint256 specificLength = specificIds.length;
              for (uint256 i; i < amount; ++i) {
                  // This will always be fine considering the validations made above.
                  uint256 tokenId = i < specificLength
                      ? specificIds[i]
                      : getRandomTokenIdFromVault();
                  redeemedIds[i] = tokenId;
                  if (_is1155) {
                      quantity1155[tokenId] -= 1;
                      if (quantity1155[tokenId] == 0) {
                          holdings.remove(tokenId);
                      }
                      IERC1155Upgradeable(_assetAddress).safeTransferFrom(
                          address(this),
                          to,
                          tokenId,
                          1,
                          ""
                      );
                  } else {
                      holdings.remove(tokenId);
                      transferERC721(_assetAddress, to, tokenId);
                  }
              }
              afterRedeemHook(redeemedIds);
              return redeemedIds;
          }
          function _chargeAndDistributeFees(address user, uint256 amount)
              internal
              virtual
          {
              // Do not charge fees if the zap contract is calling
              // Added in v1.0.3. Changed to mapping in v1.0.5.
              INFTXVaultFactory _vaultFactory = vaultFactory;
              if (_vaultFactory.excludedFromFees(msg.sender)) {
                  return;
              }
              // Mint fees directly to the distributor and distribute.
              if (amount > 0) {
                  address feeDistributor = _vaultFactory.feeDistributor();
                  // Changed to a _transfer() in v1.0.3.
                  _transfer(user, feeDistributor, amount);
                  INFTXFeeDistributor(feeDistributor).distribute(vaultId);
              }
          }
          function transferERC721(
              address assetAddr,
              address to,
              uint256 tokenId
          ) internal virtual {
              address kitties = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
              address punks = 0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB;
              bytes memory data;
              if (assetAddr == kitties) {
                  // Changed in v1.0.4.
                  data = abi.encodeWithSignature(
                      "transfer(address,uint256)",
                      to,
                      tokenId
                  );
              } else if (assetAddr == punks) {
                  // CryptoPunks.
                  data = abi.encodeWithSignature(
                      "transferPunk(address,uint256)",
                      to,
                      tokenId
                  );
              } else {
                  // Default.
                  data = abi.encodeWithSignature(
                      "safeTransferFrom(address,address,uint256)",
                      address(this),
                      to,
                      tokenId
                  );
              }
              (bool success, bytes memory returnData) = address(assetAddr).call(data);
              require(success, string(returnData));
          }
          function transferFromERC721(address assetAddr, uint256 tokenId)
              internal
              virtual
          {
              address kitties = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
              address punks = 0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB;
              bytes memory data;
              if (assetAddr == kitties) {
                  // Cryptokitties.
                  data = abi.encodeWithSignature(
                      "transferFrom(address,address,uint256)",
                      msg.sender,
                      address(this),
                      tokenId
                  );
              } else if (assetAddr == punks) {
                  // CryptoPunks.
                  // Fix here for frontrun attack. Added in v1.0.2.
                  bytes memory punkIndexToAddress = abi.encodeWithSignature(
                      "punkIndexToAddress(uint256)",
                      tokenId
                  );
                  (bool checkSuccess, bytes memory result) = address(assetAddr)
                      .staticcall(punkIndexToAddress);
                  address nftOwner = abi.decode(result, (address));
                  require(
                      checkSuccess && nftOwner == msg.sender,
                      "Not the NFT owner"
                  );
                  data = abi.encodeWithSignature("buyPunk(uint256)", tokenId);
              } else {
                  // Default.
                  // Allow other contracts to "push" into the vault, safely.
                  // If we already have the token requested, make sure we don't have it in the list to prevent duplicate minting.
                  if (
                      IERC721Upgradeable(assetAddress).ownerOf(tokenId) ==
                      address(this)
                  ) {
                      require(
                          !holdings.contains(tokenId),
                          "Trying to use an owned NFT"
                      );
                      return;
                  } else {
                      data = abi.encodeWithSignature(
                          "safeTransferFrom(address,address,uint256)",
                          msg.sender,
                          address(this),
                          tokenId
                      );
                  }
              }
              (bool success, bytes memory resultData) = address(assetAddr).call(data);
              require(success, string(resultData));
          }
          function getRandomTokenIdFromVault() internal virtual returns (uint256) {
              uint256 randomIndex = uint256(
                  keccak256(
                      abi.encodePacked(
                          blockhash(block.number - 1),
                          randNonce,
                          block.coinbase,
                          block.difficulty,
                          block.timestamp
                      )
                  )
              ) % holdings.length();
              ++randNonce;
              return holdings.at(randomIndex);
          }
          function onlyPrivileged() internal view {
              if (manager == address(0)) {
                  require(msg.sender == owner(), "Not owner");
              } else {
                  require(msg.sender == manager, "Not manager");
              }
          }
          function onlyOwnerIfPaused(uint256 lockId) internal view {
              require(
                  !vaultFactory.isLocked(lockId) || msg.sender == owner(),
                  "Paused"
              );
          }
      }
      // SPDX-License-Identifier: MIT
      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
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC3156 FlashBorrower, as defined in
       * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
       */
      interface IERC3156FlashBorrowerUpgradeable {
          /**
           * @dev Receive a flash loan.
           * @param initiator The initiator of the loan.
           * @param token The loan currency.
           * @param amount The amount of tokens lent.
           * @param fee The additional amount of tokens to repay.
           * @param data Arbitrary data structure, intended to contain user-defined parameters.
           * @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan"
           */
          function onFlashLoan(
              address initiator,
              address token,
              uint256 amount,
              uint256 fee,
              bytes calldata data
          ) external returns (bytes32);
      }
      /**
       * @dev Interface of the ERC3156 FlashLender, as defined in
       * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
       */
      interface IERC3156FlashLenderUpgradeable {
          /**
           * @dev The amount of currency available to be lended.
           * @param token The loan currency.
           * @return The amount of `token` that can be borrowed.
           */
          function maxFlashLoan(address token) external view returns (uint256);
          /**
           * @dev The fee to be charged for a given loan.
           * @param token The loan currency.
           * @param amount The amount of tokens lent.
           * @return The amount of `token` to be charged for the loan, on top of the returned principal.
           */
          function flashFee(address token, uint256 amount)
              external
              view
              returns (uint256);
          /**
           * @dev Initiate a flash loan.
           * @param receiver The receiver of the tokens in the loan, and the receiver of the callback.
           * @param token The loan currency.
           * @param amount The amount of tokens lent.
           * @param data Arbitrary data structure, intended to contain user-defined parameters.
           */
          function flashLoan(
              IERC3156FlashBorrowerUpgradeable receiver,
              address token,
              uint256 amount,
              bytes calldata data
          ) external returns (bool);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      interface INFTXEligibility {
          // Read functions.
          function name() external pure returns (string memory);
          function finalized() external view returns (bool);
          function targetAsset() external pure returns (address);
          function checkAllEligible(uint256[] calldata tokenIds)
              external
              view
              returns (bool);
          function checkEligible(uint256[] calldata tokenIds)
              external
              view
              returns (bool[] memory);
          function checkAllIneligible(uint256[] calldata tokenIds)
              external
              view
              returns (bool);
          function checkIsEligible(uint256 tokenId) external view returns (bool);
          // Write functions.
          function __NFTXEligibility_init_bytes(bytes calldata configData) external;
          function beforeMintHook(uint256[] calldata tokenIds) external;
          function afterMintHook(uint256[] calldata tokenIds) external;
          function beforeRedeemHook(uint256[] calldata tokenIds) external;
          function afterRedeemHook(uint256[] calldata tokenIds) external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      interface INFTXEligibilityManager {
          function nftxVaultFactory() external returns (address);
          function eligibilityImpl() external returns (address);
          function deployEligibility(uint256 vaultId, bytes calldata initData)
              external
              returns (address);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      interface INFTXFeeDistributor {
          struct FeeReceiver {
              uint256 allocPoint;
              address receiver;
              bool isContract;
          }
          function nftxVaultFactory() external returns (address);
          function lpStaking() external returns (address);
          function treasury() external returns (address);
          function defaultTreasuryAlloc() external returns (uint256);
          function defaultLPAlloc() external returns (uint256);
          function allocTotal(uint256 vaultId) external returns (uint256);
          function specificTreasuryAlloc(uint256 vaultId) external returns (uint256);
          // Write functions.
          function __FeeDistributor__init__(address _lpStaking, address _treasury)
              external;
          function rescueTokens(address token) external;
          function distribute(uint256 vaultId) external;
          function addReceiver(
              uint256 _vaultId,
              uint256 _allocPoint,
              address _receiver,
              bool _isContract
          ) external;
          function initializeVaultReceivers(uint256 _vaultId) external;
          function changeMultipleReceiverAlloc(
              uint256[] memory _vaultIds,
              uint256[] memory _receiverIdxs,
              uint256[] memory allocPoints
          ) external;
          function changeMultipleReceiverAddress(
              uint256[] memory _vaultIds,
              uint256[] memory _receiverIdxs,
              address[] memory addresses,
              bool[] memory isContracts
          ) external;
          function changeReceiverAlloc(
              uint256 _vaultId,
              uint256 _idx,
              uint256 _allocPoint
          ) external;
          function changeReceiverAddress(
              uint256 _vaultId,
              uint256 _idx,
              address _address,
              bool _isContract
          ) external;
          function removeReceiver(uint256 _vaultId, uint256 _receiverIdx) external;
          // Configuration functions.
          function setTreasuryAddress(address _treasury) external;
          function setDefaultTreasuryAlloc(uint256 _allocPoint) external;
          function setSpecificTreasuryAlloc(uint256 _vaultId, uint256 _allocPoint)
              external;
          function setLPStakingAddress(address _lpStaking) external;
          function setNFTXVaultFactory(address _factory) external;
          function setDefaultLPAlloc(uint256 _allocPoint) external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../token/IERC20Upgradeable.sol";
      import "./INFTXVaultFactory.sol";
      import "./INFTXEligibility.sol";
      interface INFTXVault is IERC20Upgradeable {
          function manager() external view returns (address);
          function assetAddress() external view returns (address);
          function vaultFactory() external view returns (INFTXVaultFactory);
          function eligibilityStorage() external view returns (INFTXEligibility);
          function is1155() external view returns (bool);
          function allowAllItems() external view returns (bool);
          function enableMint() external view returns (bool);
          function enableRandomRedeem() external view returns (bool);
          function enableTargetRedeem() external view returns (bool);
          function enableRandomSwap() external view returns (bool);
          function enableTargetSwap() external view returns (bool);
          function vaultId() external view returns (uint256);
          function nftIdAt(uint256 holdingsIndex) external view returns (uint256);
          function allHoldings() external view returns (uint256[] memory);
          function totalHoldings() external view returns (uint256);
          function mintFee() external view returns (uint256);
          function randomRedeemFee() external view returns (uint256);
          function targetRedeemFee() external view returns (uint256);
          function randomSwapFee() external view returns (uint256);
          function targetSwapFee() external view returns (uint256);
          function vaultFees()
              external
              view
              returns (
                  uint256,
                  uint256,
                  uint256,
                  uint256,
                  uint256
              );
          event VaultInit(
              uint256 indexed vaultId,
              address assetAddress,
              bool is1155,
              bool allowAllItems
          );
          event ManagerSet(address manager);
          event EligibilityDeployed(uint256 moduleIndex, address eligibilityAddr);
          // event CustomEligibilityDeployed(address eligibilityAddr);
          event EnableMintUpdated(bool enabled);
          event EnableRandomRedeemUpdated(bool enabled);
          event EnableTargetRedeemUpdated(bool enabled);
          event EnableRandomSwapUpdated(bool enabled);
          event EnableTargetSwapUpdated(bool enabled);
          event Minted(uint256[] nftIds, uint256[] amounts, address to);
          event Redeemed(uint256[] nftIds, uint256[] specificIds, address to);
          event Swapped(
              uint256[] nftIds,
              uint256[] amounts,
              uint256[] specificIds,
              uint256[] redeemedIds,
              address to
          );
          function __NFTXVault_init(
              string calldata _name,
              string calldata _symbol,
              address _assetAddress,
              bool _is1155,
              bool _allowAllItems
          ) external;
          function finalizeVault() external;
          function setVaultMetadata(string memory name_, string memory symbol_)
              external;
          function setVaultFeatures(
              bool _enableMint,
              bool _enableRandomRedeem,
              bool _enableTargetRedeem,
              bool _enableRandomSwap,
              bool _enableTargetSwap
          ) external;
          function setFees(
              uint256 _mintFee,
              uint256 _randomRedeemFee,
              uint256 _targetRedeemFee,
              uint256 _randomSwapFee,
              uint256 _targetSwapFee
          ) external;
          function disableVaultFees() external;
          // This function allows for an easy setup of any eligibility module contract from the EligibilityManager.
          // It takes in ABI encoded parameters for the desired module. This is to make sure they can all follow
          // a similar interface.
          function deployEligibilityStorage(
              uint256 moduleIndex,
              bytes calldata initData
          ) external returns (address);
          // The manager has control over options like fees and features
          function setManager(address _manager) external;
          function mint(
              uint256[] calldata tokenIds,
              uint256[] calldata amounts /* ignored for ERC721 vaults */
          ) external returns (uint256);
          function mintTo(
              uint256[] calldata tokenIds,
              uint256[] calldata amounts, /* ignored for ERC721 vaults */
              address to
          ) external returns (uint256);
          function redeem(uint256 amount, uint256[] calldata specificIds)
              external
              returns (uint256[] calldata);
          function redeemTo(
              uint256 amount,
              uint256[] calldata specificIds,
              address to
          ) external returns (uint256[] calldata);
          function swap(
              uint256[] calldata tokenIds,
              uint256[] calldata amounts, /* ignored for ERC721 vaults */
              uint256[] calldata specificIds
          ) external returns (uint256[] calldata);
          function swapTo(
              uint256[] calldata tokenIds,
              uint256[] calldata amounts, /* ignored for ERC721 vaults */
              uint256[] calldata specificIds,
              address to
          ) external returns (uint256[] calldata);
          function allValidNFTs(uint256[] calldata tokenIds)
              external
              view
              returns (bool);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../proxy/IBeacon.sol";
      interface INFTXVaultFactory is IBeacon {
          // Read functions.
          function numVaults() external view returns (uint256);
          function zapContract() external view returns (address);
          function zapContracts(address addr) external view returns (bool);
          function feeDistributor() external view returns (address);
          function eligibilityManager() external view returns (address);
          function vault(uint256 vaultId) external view returns (address);
          function allVaults() external view returns (address[] memory);
          function vaultsForAsset(address asset)
              external
              view
              returns (address[] memory);
          function isLocked(uint256 id) external view returns (bool);
          function excludedFromFees(address addr) external view returns (bool);
          function factoryMintFee() external view returns (uint64);
          function factoryRandomRedeemFee() external view returns (uint64);
          function factoryTargetRedeemFee() external view returns (uint64);
          function factoryRandomSwapFee() external view returns (uint64);
          function factoryTargetSwapFee() external view returns (uint64);
          function vaultFees(uint256 vaultId)
              external
              view
              returns (
                  uint256,
                  uint256,
                  uint256,
                  uint256,
                  uint256
              );
          event NewFeeDistributor(address oldDistributor, address newDistributor);
          event NewZapContract(address oldZap, address newZap);
          event UpdatedZapContract(address zap, bool excluded);
          event FeeExclusion(address feeExcluded, bool excluded);
          event NewEligibilityManager(address oldEligManager, address newEligManager);
          event NewVault(
              uint256 indexed vaultId,
              address vaultAddress,
              address assetAddress
          );
          event UpdateVaultFees(
              uint256 vaultId,
              uint256 mintFee,
              uint256 randomRedeemFee,
              uint256 targetRedeemFee,
              uint256 randomSwapFee,
              uint256 targetSwapFee
          );
          event DisableVaultFees(uint256 vaultId);
          event UpdateFactoryFees(
              uint256 mintFee,
              uint256 randomRedeemFee,
              uint256 targetRedeemFee,
              uint256 randomSwapFee,
              uint256 targetSwapFee
          );
          // Write functions.
          function __NFTXVaultFactory_init(
              address _vaultImpl,
              address _feeDistributor
          ) external;
          function createVault(
              string calldata name,
              string calldata symbol,
              address _assetAddress,
              bool is1155,
              bool allowAllItems
          ) external returns (uint256);
          function setFeeDistributor(address _feeDistributor) external;
          function setEligibilityManager(address _eligibilityManager) external;
          function setZapContract(address _zapContract, bool _excluded) external;
          function setFeeExclusion(address _excludedAddr, bool excluded) external;
          function setFactoryFees(
              uint256 mintFee,
              uint256 randomRedeemFee,
              uint256 targetRedeemFee,
              uint256 randomSwapFee,
              uint256 targetSwapFee
          ) external;
          function setVaultFees(
              uint256 vaultId,
              uint256 mintFee,
              uint256 randomRedeemFee,
              uint256 targetRedeemFee,
              uint256 randomSwapFee,
              uint256 targetSwapFee
          ) external;
          function disableVaultFees(uint256 vaultId) external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "./Proxy.sol";
      import "./IBeacon.sol";
      import "../util/Address.sol";
      /**
       * @dev This contract implements a proxy that gets the implementation address for each call from a {UpgradeableBeacon}.
       *
       * The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't
       * conflict with the storage layout of the implementation behind the proxy.
       *
       * _Available since v3.4._
       */
      contract BeaconProxy is Proxy {
          /**
           * @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 private constant _BEACON_SLOT =
              0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
          /**
           * @dev Initializes the proxy with `beacon`.
           *
           * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This
           * will typically be an encoded function call, and allows initializating the storage of the proxy like a Solidity
           * constructor.
           *
           * Requirements:
           *
           * - `beacon` must be a contract with the interface {IBeacon}.
           */
          constructor(address beacon, bytes memory data) payable {
              assert(
                  _BEACON_SLOT ==
                      bytes32(uint256(keccak256("eip1967.proxy.beacon")) - 1)
              );
              _setBeacon(beacon, data);
          }
          /**
           * @dev Returns the current beacon address.
           */
          function _beacon() internal view virtual returns (address beacon) {
              bytes32 slot = _BEACON_SLOT;
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  beacon := sload(slot)
              }
          }
          /**
           * @dev Returns the current implementation address of the associated beacon.
           */
          function _implementation()
              internal
              view
              virtual
              override
              returns (address)
          {
              return IBeacon(_beacon()).childImplementation();
          }
          /**
           * @dev Changes the proxy to use a new beacon.
           *
           * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon.
           *
           * Requirements:
           *
           * - `beacon` must be a contract.
           * - The implementation returned by `beacon` must be a contract.
           */
          function _setBeacon(address beacon, bytes memory data) internal virtual {
              require(
                  Address.isContract(beacon),
                  "BeaconProxy: beacon is not a contract"
              );
              require(
                  Address.isContract(IBeacon(beacon).childImplementation()),
                  "BeaconProxy: beacon implementation is not a contract"
              );
              bytes32 slot = _BEACON_SLOT;
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  sstore(slot, beacon)
              }
              if (data.length > 0) {
                  Address.functionDelegateCall(
                      _implementation(),
                      data,
                      "BeaconProxy: function call failed"
                  );
              }
          }
      }
      // 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 childImplementation() external view returns (address);
          function implementation() external view returns (address);
          function upgradeChildTo(address newImplementation) external;
      }
      // SPDX-License-Identifier: MIT
      // solhint-disable-next-line compiler-version
      pragma solidity ^0.8.0;
      /**
       * @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 {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.
       */
      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 || !_initialized,
                  "Initializable: contract is already initialized"
              );
              bool isTopLevelCall = !_initializing;
              if (isTopLevelCall) {
                  _initializing = true;
                  _initialized = true;
              }
              _;
              if (isTopLevelCall) {
                  _initializing = false;
              }
          }
      }
      // 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.0;
      import "./IBeacon.sol";
      import "../util/Address.sol";
      import "../util/OwnableUpgradeable.sol";
      /**
       * @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their
       * implementation contract, which is where they will delegate all function calls.
       *
       * An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.
       */
      contract UpgradeableBeacon is IBeacon, OwnableUpgradeable {
          address private _childImplementation;
          /**
           * @dev Emitted when the child implementation returned by the beacon is changed.
           */
          event Upgraded(address indexed childImplementation);
          /**
           * @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the
           * beacon.
           */
          function __UpgradeableBeacon__init(address childImplementation_)
              public
              initializer
          {
              _setChildImplementation(childImplementation_);
          }
          /**
           * @dev Returns the current child implementation address.
           */
          function childImplementation()
              public
              view
              virtual
              override
              returns (address)
          {
              return _childImplementation;
          }
          function implementation() public view virtual override returns (address) {
              return _childImplementation;
          }
          /**
           * @dev Upgrades the beacon to a new implementation.
           *
           * Emits an {Upgraded} event.
           *
           * Requirements:
           *
           * - msg.sender must be the owner of the contract.
           * - `newChildImplementation` must be a contract.
           */
          function upgradeChildTo(address newChildImplementation)
              public
              virtual
              override
              onlyOwner
          {
              _setChildImplementation(newChildImplementation);
          }
          /**
           * @dev Sets the implementation contract address for this beacon
           *
           * Requirements:
           *
           * - `newChildImplementation` must be a contract.
           */
          function _setChildImplementation(address newChildImplementation) private {
              require(
                  Address.isContract(newChildImplementation),
                  "UpgradeableBeacon: child implementation is not a contract"
              );
              _childImplementation = newChildImplementation;
              emit Upgraded(newChildImplementation);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../util/ERC165Upgradeable.sol";
      import "./IERC1155ReceiverUpgradeable.sol";
      /**
       * @dev _Available since v3.1._
       */
      abstract contract ERC1155ReceiverUpgradeable is
          ERC165Upgradeable,
          IERC1155ReceiverUpgradeable
      {
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId)
              public
              view
              virtual
              override(ERC165Upgradeable, IERC165Upgradeable)
              returns (bool)
          {
              return
                  interfaceId == type(IERC1155ReceiverUpgradeable).interfaceId ||
                  super.supportsInterface(interfaceId);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "./ERC1155ReceiverUpgradeable.sol";
      /**
       * @dev _Available since v3.1._
       */
      abstract contract ERC1155SafeHolderUpgradeable is ERC1155ReceiverUpgradeable {
          function onERC1155Received(
              address operator,
              address,
              uint256,
              uint256,
              bytes memory
          ) public virtual override returns (bytes4) {
              return this.onERC1155Received.selector;
          }
          function onERC1155BatchReceived(
              address operator,
              address,
              uint256[] memory,
              uint256[] memory,
              bytes memory
          ) public virtual override returns (bytes4) {
              return this.onERC1155BatchReceived.selector;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../proxy/Initializable.sol";
      import "../interface/IERC3156Upgradeable.sol";
      import "./ERC20Upgradeable.sol";
      /**
       * @dev Implementation of the ERC3156 Flash loans extension, as defined in
       * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
       *
       * Adds the {flashLoan} method, which provides flash loan support at the token
       * level. By default there is no fee, but this can be changed by overriding {flashFee}.
       */
      abstract contract ERC20FlashMintUpgradeable is
          Initializable,
          ERC20Upgradeable,
          IERC3156FlashLenderUpgradeable
      {
          function __ERC20FlashMint_init() internal initializer {
              __Context_init_unchained();
              __ERC20FlashMint_init_unchained();
          }
          function __ERC20FlashMint_init_unchained() internal initializer {}
          bytes32 private constant RETURN_VALUE =
              keccak256("ERC3156FlashBorrower.onFlashLoan");
          /**
           * @dev Returns the maximum amount of tokens available for loan.
           * @param token The address of the token that is requested.
           * @return The amont of token that can be loaned.
           */
          function maxFlashLoan(address token)
              public
              view
              override
              returns (uint256)
          {
              return token == address(this) ? type(uint256).max - totalSupply() : 0;
          }
          /**
           * @dev Returns the fee applied when doing flash loans. By default this
           * implementation has 0 fees. This function can be overloaded to make
           * the flash loan mechanism deflationary.
           * @param token The token to be flash loaned.
           * @param amount The amount of tokens to be loaned.
           * @return The fees applied to the corresponding flash loan.
           */
          function flashFee(address token, uint256 amount)
              public
              view
              virtual
              override
              returns (uint256)
          {
              require(token == address(this), "ERC20FlashMint: wrong token");
              // silence warning about unused variable without the addition of bytecode.
              amount;
              return 0;
          }
          /**
           * @dev Performs a flash loan. New tokens are minted and sent to the
           * `receiver`, who is required to implement the {IERC3156FlashBorrower}
           * interface. By the end of the flash loan, the receiver is expected to own
           * amount + fee tokens and have them approved back to the token contract itself so
           * they can be burned.
           * @param receiver The receiver of the flash loan. Should implement the
           * {IERC3156FlashBorrower.onFlashLoan} interface.
           * @param token The token to be flash loaned. Only `address(this)` is
           * supported.
           * @param amount The amount of tokens to be loaned.
           * @param data An arbitrary datafield that is passed to the receiver.
           * @return `true` is the flash loan was successfull.
           */
          function flashLoan(
              IERC3156FlashBorrowerUpgradeable receiver,
              address token,
              uint256 amount,
              bytes memory data
          ) public virtual override returns (bool) {
              uint256 fee = flashFee(token, amount);
              _mint(address(receiver), amount);
              require(
                  receiver.onFlashLoan(msg.sender, token, amount, fee, data) ==
                      RETURN_VALUE,
                  "ERC20FlashMint: invalid return value"
              );
              uint256 currentAllowance = allowance(address(receiver), address(this));
              require(
                  currentAllowance >= amount + fee,
                  "ERC20FlashMint: allowance does not allow refund"
              );
              _approve(
                  address(receiver),
                  address(this),
                  currentAllowance - amount - fee
              );
              _burn(address(receiver), amount + fee);
              return true;
          }
          uint256[50] private __gap;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../proxy/Initializable.sol";
      import "../util/ContextUpgradeable.sol";
      import "./IERC20Upgradeable.sol";
      import "./IERC20Metadata.sol";
      /**
       * @dev Implementation of the {IERC20} interface.
       *
       * This implementation is agnostic to the way tokens are created. This means
       * that a supply mechanism has to be added in a derived contract using {_mint}.
       * For a generic mechanism see {ERC20PresetMinterPauser}.
       *
       * TIP: For a detailed writeup see our guide
       * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
       * to implement supply mechanisms].
       *
       * We have followed general OpenZeppelin guidelines: functions revert instead
       * of returning `false` on failure. This behavior is nonetheless conventional
       * and does not conflict with the expectations of ERC20 applications.
       *
       * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
       * This allows applications to reconstruct the allowance for all accounts just
       * by listening to said events. Other implementations of the EIP may not emit
       * these events, as it isn't required by the specification.
       *
       * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
       * functions have been added to mitigate the well-known issues around setting
       * allowances. See {IERC20-approve}.
       */
      contract ERC20Upgradeable is
          Initializable,
          ContextUpgradeable,
          IERC20Upgradeable,
          IERC20Metadata
      {
          mapping(address => uint256) private _balances;
          mapping(address => mapping(address => uint256)) private _allowances;
          uint256 private _totalSupply;
          string private _name;
          string private _symbol;
          /**
           * @dev Sets the values for {name} and {symbol}.
           *
           * The default value of {decimals} is 18. To select a different value for
           * {decimals} you should overload it.
           *
           * All two of these values are immutable: they can only be set once during
           * construction.
           */
          function __ERC20_init(string memory name_, string memory symbol_)
              internal
              initializer
          {
              __Context_init_unchained();
              __ERC20_init_unchained(name_, symbol_);
          }
          function __ERC20_init_unchained(string memory name_, string memory symbol_)
              internal
              initializer
          {
              _name = name_;
              _symbol = symbol_;
          }
          function _setMetadata(string memory name_, string memory symbol_) internal {
              _name = name_;
              _symbol = symbol_;
          }
          /**
           * @dev Returns the name of the token.
           */
          function name() public view virtual override returns (string memory) {
              return _name;
          }
          /**
           * @dev Returns the symbol of the token, usually a shorter version of the
           * name.
           */
          function symbol() public view virtual override returns (string memory) {
              return _symbol;
          }
          /**
           * @dev Returns the number of decimals used to get its user representation.
           * For example, if `decimals` equals `2`, a balance of `505` tokens should
           * be displayed to a user as `5,05` (`505 / 10 ** 2`).
           *
           * Tokens usually opt for a value of 18, imitating the relationship between
           * Ether and Wei. This is the value {ERC20} uses, unless this function is
           * overridden;
           *
           * NOTE: This information is only used for _display_ purposes: it in
           * no way affects any of the arithmetic of the contract, including
           * {IERC20-balanceOf} and {IERC20-transfer}.
           */
          function decimals() public view virtual override returns (uint8) {
              return 18;
          }
          /**
           * @dev See {IERC20-totalSupply}.
           */
          function totalSupply() public view virtual override returns (uint256) {
              return _totalSupply;
          }
          /**
           * @dev See {IERC20-balanceOf}.
           */
          function balanceOf(address account)
              public
              view
              virtual
              override
              returns (uint256)
          {
              return _balances[account];
          }
          /**
           * @dev See {IERC20-transfer}.
           *
           * Requirements:
           *
           * - `recipient` cannot be the zero address.
           * - the caller must have a balance of at least `amount`.
           */
          function transfer(address recipient, uint256 amount)
              public
              virtual
              override
              returns (bool)
          {
              _transfer(_msgSender(), recipient, amount);
              return true;
          }
          /**
           * @dev See {IERC20-allowance}.
           */
          function allowance(address owner, address spender)
              public
              view
              virtual
              override
              returns (uint256)
          {
              return _allowances[owner][spender];
          }
          /**
           * @dev See {IERC20-approve}.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          function approve(address spender, uint256 amount)
              public
              virtual
              override
              returns (bool)
          {
              _approve(_msgSender(), spender, amount);
              return true;
          }
          /**
           * @dev See {IERC20-transferFrom}.
           *
           * Emits an {Approval} event indicating the updated allowance. This is not
           * required by the EIP. See the note at the beginning of {ERC20}.
           *
           * Requirements:
           *
           * - `sender` and `recipient` cannot be the zero address.
           * - `sender` must have a balance of at least `amount`.
           * - the caller must have allowance for ``sender``'s tokens of at least
           * `amount`.
           */
          function transferFrom(
              address sender,
              address recipient,
              uint256 amount
          ) public virtual override returns (bool) {
              _transfer(sender, recipient, amount);
              uint256 currentAllowance = _allowances[sender][_msgSender()];
              require(
                  currentAllowance >= amount,
                  "ERC20: transfer amount exceeds allowance"
              );
              _approve(sender, _msgSender(), currentAllowance - amount);
              return true;
          }
          /**
           * @dev Atomically increases the allowance granted to `spender` by the caller.
           *
           * This is an alternative to {approve} that can be used as a mitigation for
           * problems described in {IERC20-approve}.
           *
           * Emits an {Approval} event indicating the updated allowance.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          function increaseAllowance(address spender, uint256 addedValue)
              public
              virtual
              returns (bool)
          {
              _approve(
                  _msgSender(),
                  spender,
                  _allowances[_msgSender()][spender] + addedValue
              );
              return true;
          }
          /**
           * @dev Atomically decreases the allowance granted to `spender` by the caller.
           *
           * This is an alternative to {approve} that can be used as a mitigation for
           * problems described in {IERC20-approve}.
           *
           * Emits an {Approval} event indicating the updated allowance.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           * - `spender` must have allowance for the caller of at least
           * `subtractedValue`.
           */
          function decreaseAllowance(address spender, uint256 subtractedValue)
              public
              virtual
              returns (bool)
          {
              uint256 currentAllowance = _allowances[_msgSender()][spender];
              require(
                  currentAllowance >= subtractedValue,
                  "ERC20: decreased allowance below zero"
              );
              _approve(_msgSender(), spender, currentAllowance - subtractedValue);
              return true;
          }
          /**
           * @dev Moves tokens `amount` from `sender` to `recipient`.
           *
           * This is internal function is equivalent to {transfer}, and can be used to
           * e.g. implement automatic token fees, slashing mechanisms, etc.
           *
           * Emits a {Transfer} event.
           *
           * Requirements:
           *
           * - `sender` cannot be the zero address.
           * - `recipient` cannot be the zero address.
           * - `sender` must have a balance of at least `amount`.
           */
          function _transfer(
              address sender,
              address recipient,
              uint256 amount
          ) internal virtual {
              require(sender != address(0), "ERC20: transfer from the zero address");
              require(recipient != address(0), "ERC20: transfer to the zero address");
              _beforeTokenTransfer(sender, recipient, amount);
              uint256 senderBalance = _balances[sender];
              require(
                  senderBalance >= amount,
                  "ERC20: transfer amount exceeds balance"
              );
              _balances[sender] = senderBalance - amount;
              _balances[recipient] += amount;
              emit Transfer(sender, recipient, amount);
          }
          /** @dev Creates `amount` tokens and assigns them to `account`, increasing
           * the total supply.
           *
           * Emits a {Transfer} event with `from` set to the zero address.
           *
           * Requirements:
           *
           * - `account` cannot be the zero address.
           */
          function _mint(address account, uint256 amount) internal virtual {
              require(account != address(0), "ERC20: mint to the zero address");
              _beforeTokenTransfer(address(0), account, amount);
              _totalSupply += amount;
              _balances[account] += amount;
              emit Transfer(address(0), account, amount);
          }
          /**
           * @dev Destroys `amount` tokens from `account`, reducing the
           * total supply.
           *
           * Emits a {Transfer} event with `to` set to the zero address.
           *
           * Requirements:
           *
           * - `account` cannot be the zero address.
           * - `account` must have at least `amount` tokens.
           */
          function _burn(address account, uint256 amount) internal virtual {
              require(account != address(0), "ERC20: burn from the zero address");
              _beforeTokenTransfer(account, address(0), amount);
              uint256 accountBalance = _balances[account];
              require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
              _balances[account] = accountBalance - amount;
              _totalSupply -= amount;
              emit Transfer(account, address(0), amount);
          }
          /**
           * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
           *
           * This internal function is equivalent to `approve`, and can be used to
           * e.g. set automatic allowances for certain subsystems, etc.
           *
           * Emits an {Approval} event.
           *
           * Requirements:
           *
           * - `owner` cannot be the zero address.
           * - `spender` cannot be the zero address.
           */
          function _approve(
              address owner,
              address spender,
              uint256 amount
          ) internal virtual {
              require(owner != address(0), "ERC20: approve from the zero address");
              require(spender != address(0), "ERC20: approve to the zero address");
              _allowances[owner][spender] = amount;
              emit Approval(owner, spender, amount);
          }
          /**
           * @dev Hook that is called before any transfer of tokens. This includes
           * minting and burning.
           *
           * Calling conditions:
           *
           * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
           * will be to transferred to `to`.
           * - when `from` is zero, `amount` tokens will be minted for `to`.
           * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
           * - `from` and `to` are never both zero.
           *
           * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
           */
          function _beforeTokenTransfer(
              address from,
              address to,
              uint256 amount
          ) internal virtual {}
          uint256[45] private __gap;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "./IERC721ReceiverUpgradeable.sol";
      /**
       * @dev Implementation of the {IERC721Receiver} interface.
       *
       * Accepts all token transfers.
       * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}.
       */
      contract ERC721SafeHolderUpgradeable is IERC721ReceiverUpgradeable {
          /**
           * @dev See {IERC721Receiver-onERC721Received}.
           *
           * Always returns `IERC721Receiver.onERC721Received.selector`.
           */
          function onERC721Received(
              address operator,
              address,
              uint256,
              bytes memory
          ) public virtual override returns (bytes4) {
              return this.onERC721Received.selector;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../interface/IERC165Upgradeable.sol";
      /**
       * @dev _Available since v3.1._
       */
      interface IERC1155ReceiverUpgradeable is IERC165Upgradeable {
          /**
              @dev Handles the receipt of a single ERC1155 token type. This function is
              called at the end of a `safeTransferFrom` after the balance has been updated.
              To accept the transfer, this must return
              `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
              (i.e. 0xf23a6e61, or its own function selector).
              @param operator The address which initiated the transfer (i.e. msg.sender)
              @param from The address which previously owned the token
              @param id The ID of the token being transferred
              @param value The amount of tokens being transferred
              @param data Additional data with no specified format
              @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
          */
          function onERC1155Received(
              address operator,
              address from,
              uint256 id,
              uint256 value,
              bytes calldata data
          ) external returns (bytes4);
          /**
              @dev Handles the receipt of a multiple ERC1155 token types. This function
              is called at the end of a `safeBatchTransferFrom` after the balances have
              been updated. To accept the transfer(s), this must return
              `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
              (i.e. 0xbc197c81, or its own function selector).
              @param operator The address which initiated the batch transfer (i.e. msg.sender)
              @param from The address which previously owned the token
              @param ids An array containing ids of each token being transferred (order and length must match values array)
              @param values An array containing amounts of each token being transferred (order and length must match ids array)
              @param data Additional data with no specified format
              @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
          */
          function onERC1155BatchReceived(
              address operator,
              address from,
              uint256[] calldata ids,
              uint256[] calldata values,
              bytes calldata data
          ) external returns (bytes4);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../interface/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.8.0;
      import "./IERC20Upgradeable.sol";
      /**
       * @dev Interface for the optional metadata functions from the ERC20 standard.
       *
       * _Available since v4.1._
       */
      interface IERC20Metadata is IERC20Upgradeable {
          /**
           * @dev Returns the name of the token.
           */
          function name() external view returns (string memory);
          /**
           * @dev Returns the symbol of the token.
           */
          function symbol() external view returns (string memory);
          /**
           * @dev Returns the decimals places of the token.
           */
          function decimals() external view returns (uint8);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^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.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 `IERC721.onERC721Received.selector`.
           */
          function onERC721Received(
              address operator,
              address from,
              uint256 tokenId,
              bytes calldata data
          ) external returns (bytes4);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../interface/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.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;
      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 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) {
              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;
          }
          uint256[50] private __gap;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../interface/IERC165Upgradeable.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 IERC165Upgradeable {
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId)
              public
              view
              virtual
              override
              returns (bool)
          {
              return interfaceId == type(IERC165Upgradeable).interfaceId;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /**
       * @dev Library for managing
       * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
       * types.
       *
       * Sets have the following properties:
       *
       * - Elements are added, removed, and checked for existence in constant time
       * (O(1)).
       * - Elements are enumerated in O(n). No guarantees are made on the ordering.
       *
       * ```
       * contract Example {
       *     // Add the library methods
       *     using EnumerableSet for EnumerableSet.AddressSet;
       *
       *     // Declare a set state variable
       *     EnumerableSet.AddressSet private mySet;
       * }
       * ```
       *
       * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
       * and `uint256` (`UintSet`) are supported.
       */
      library EnumerableSetUpgradeable {
          // To implement this library for multiple types with as little code
          // repetition as possible, we write it in terms of a generic Set type with
          // bytes32 values.
          // The Set implementation uses private functions, and user-facing
          // implementations (such as AddressSet) are just wrappers around the
          // underlying Set.
          // This means that we can only create new EnumerableSets for types that fit
          // in bytes32.
          struct Set {
              // Storage of set values
              bytes32[] _values;
              // Position of the value in the `values` array, plus 1 because index 0
              // means a value is not in the set.
              mapping(bytes32 => uint256) _indexes;
          }
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function _add(Set storage set, bytes32 value) private returns (bool) {
              if (!_contains(set, value)) {
                  set._values.push(value);
                  // The value is stored at length-1, but we add 1 to all indexes
                  // and use 0 as a sentinel value
                  set._indexes[value] = set._values.length;
                  return true;
              } else {
                  return false;
              }
          }
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function _remove(Set storage set, bytes32 value) private returns (bool) {
              // We read and store the value's index to prevent multiple reads from the same storage slot
              uint256 valueIndex = set._indexes[value];
              if (valueIndex != 0) {
                  // Equivalent to contains(set, value)
                  // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                  // the array, and then remove the last element (sometimes called as 'swap and pop').
                  // This modifies the order of the array, as noted in {at}.
                  uint256 toDeleteIndex = valueIndex - 1;
                  uint256 lastIndex = set._values.length - 1;
                  if (lastIndex != toDeleteIndex) {
                      bytes32 lastvalue = set._values[lastIndex];
                      // Move the last value to the index where the value to delete is
                      set._values[toDeleteIndex] = lastvalue;
                      // Update the index for the moved value
                      set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex
                  }
                  // Delete the slot where the moved value was stored
                  set._values.pop();
                  // Delete the index for the deleted slot
                  delete set._indexes[value];
                  return true;
              } else {
                  return false;
              }
          }
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function _contains(Set storage set, bytes32 value)
              private
              view
              returns (bool)
          {
              return set._indexes[value] != 0;
          }
          /**
           * @dev Returns the number of values on the set. O(1).
           */
          function _length(Set storage set) private view returns (uint256) {
              return set._values.length;
          }
          /**
           * @dev Returns the value stored at position `index` in the set. O(1).
           *
           * Note that there are no guarantees on the ordering of values inside the
           * array, and it may change when more values are added or removed.
           *
           * Requirements:
           *
           * - `index` must be strictly less than {length}.
           */
          function _at(Set storage set, uint256 index)
              private
              view
              returns (bytes32)
          {
              return set._values[index];
          }
          // Bytes32Set
          struct Bytes32Set {
              Set _inner;
          }
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(Bytes32Set storage set, bytes32 value)
              internal
              returns (bool)
          {
              return _add(set._inner, value);
          }
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(Bytes32Set storage set, bytes32 value)
              internal
              returns (bool)
          {
              return _remove(set._inner, value);
          }
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(Bytes32Set storage set, bytes32 value)
              internal
              view
              returns (bool)
          {
              return _contains(set._inner, value);
          }
          /**
           * @dev Returns the number of values in the set. O(1).
           */
          function length(Bytes32Set storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
          /**
           * @dev Returns the value stored at position `index` in the set. O(1).
           *
           * Note that there are no guarantees on the ordering of values inside the
           * array, and it may change when more values are added or removed.
           *
           * Requirements:
           *
           * - `index` must be strictly less than {length}.
           */
          function at(Bytes32Set storage set, uint256 index)
              internal
              view
              returns (bytes32)
          {
              return _at(set._inner, index);
          }
          // AddressSet
          struct AddressSet {
              Set _inner;
          }
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(AddressSet storage set, address value)
              internal
              returns (bool)
          {
              return _add(set._inner, bytes32(uint256(uint160(value))));
          }
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(AddressSet storage set, address value)
              internal
              returns (bool)
          {
              return _remove(set._inner, bytes32(uint256(uint160(value))));
          }
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(AddressSet storage set, address value)
              internal
              view
              returns (bool)
          {
              return _contains(set._inner, bytes32(uint256(uint160(value))));
          }
          /**
           * @dev Returns the number of values in the set. O(1).
           */
          function length(AddressSet storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
          /**
           * @dev Returns the value stored at position `index` in the set. O(1).
           *
           * Note that there are no guarantees on the ordering of values inside the
           * array, and it may change when more values are added or removed.
           *
           * Requirements:
           *
           * - `index` must be strictly less than {length}.
           */
          function at(AddressSet storage set, uint256 index)
              internal
              view
              returns (address)
          {
              return address(uint160(uint256(_at(set._inner, index))));
          }
          // UintSet
          struct UintSet {
              Set _inner;
          }
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(UintSet storage set, uint256 value) internal returns (bool) {
              return _add(set._inner, bytes32(value));
          }
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(UintSet storage set, uint256 value)
              internal
              returns (bool)
          {
              return _remove(set._inner, bytes32(value));
          }
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(UintSet storage set, uint256 value)
              internal
              view
              returns (bool)
          {
              return _contains(set._inner, bytes32(value));
          }
          /**
           * @dev Returns the number of values on the set. O(1).
           */
          function length(UintSet storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
          /**
           * @dev Returns the value stored at position `index` in the set. O(1).
           *
           * Note that there are no guarantees on the ordering of values inside the
           * array, and it may change when more values are added or removed.
           *
           * Requirements:
           *
           * - `index` must be strictly less than {length}.
           */
          function at(UintSet storage set, uint256 index)
              internal
              view
              returns (uint256)
          {
              return uint256(_at(set._inner, index));
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../proxy/Initializable.sol";
      import "./ContextUpgradeable.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.8.0;
      import "./OwnableUpgradeable.sol";
      contract PausableUpgradeable is OwnableUpgradeable {
          function __Pausable_init() internal initializer {
              __Ownable_init();
          }
          event SetPaused(uint256 lockId, bool paused);
          event SetIsGuardian(address addr, bool isGuardian);
          mapping(address => bool) public isGuardian;
          mapping(uint256 => bool) public isPaused;
          // 0 : createVault
          // 1 : mint
          // 2 : redeem
          // 3 : swap
          // 4 : flashloan
          function onlyOwnerIfPaused(uint256 lockId) public view virtual {
              require(!isPaused[lockId] || msg.sender == owner(), "Paused");
          }
          function unpause(uint256 lockId) public virtual onlyOwner {
              isPaused[lockId] = false;
              emit SetPaused(lockId, false);
          }
          function pause(uint256 lockId) public virtual {
              require(isGuardian[msg.sender], "Can't pause");
              isPaused[lockId] = true;
              emit SetPaused(lockId, true);
          }
          function setIsGuardian(address addr, bool _isGuardian)
              public
              virtual
              onlyOwner
          {
              isGuardian[addr] = _isGuardian;
              emit SetIsGuardian(addr, _isGuardian);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../proxy/Initializable.sol";
      /**
       * @dev Contract module that helps prevent reentrant calls to a function.
       *
       * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
       * available, which can be applied to functions to make sure there are no nested
       * (reentrant) calls to them.
       *
       * Note that because there is a single `nonReentrant` guard, functions marked as
       * `nonReentrant` may not call one another. This can be worked around by making
       * those functions `private`, and then adding `external` `nonReentrant` entry
       * points to them.
       *
       * TIP: If you would like to learn more about reentrancy and alternative ways
       * to protect against it, check out our blog post
       * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
       */
      abstract contract ReentrancyGuardUpgradeable is Initializable {
          // Booleans are more expensive than uint256 or any type that takes up a full
          // word because each write operation emits an extra SLOAD to first read the
          // slot's contents, replace the bits taken up by the boolean, and then write
          // back. This is the compiler's defense against contract upgrades and
          // pointer aliasing, and it cannot be disabled.
          // The values being non-zero value makes deployment a bit more expensive,
          // but in exchange the refund on every call to nonReentrant will be lower in
          // amount. Since refunds are capped to a percentage of the total
          // transaction's gas, it is best to keep them low in cases like this one, to
          // increase the likelihood of the full refund coming into effect.
          uint256 private constant _NOT_ENTERED = 1;
          uint256 private constant _ENTERED = 2;
          uint256 private _status;
          function __ReentrancyGuard_init() internal initializer {
              __ReentrancyGuard_init_unchained();
          }
          function __ReentrancyGuard_init_unchained() internal initializer {
              _status = _NOT_ENTERED;
          }
          /**
           * @dev Prevents a contract from calling itself, directly or indirectly.
           * Calling a `nonReentrant` function from another `nonReentrant`
           * function is not supported. It is possible to prevent this from happening
           * by making the `nonReentrant` function external, and make it call a
           * `private` function that does the actual work.
           */
          modifier nonReentrant() {
              // On the first call to nonReentrant, _notEntered will be true
              require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
              // Any calls to nonReentrant after this point will fail
              _status = _ENTERED;
              _;
              // By storing the original value once again, a refund is triggered (see
              // https://eips.ethereum.org/EIPS/eip-2200)
              _status = _NOT_ENTERED;
          }
          uint256[49] private __gap;
      }
      

      File 4 of 4: NFTXVaultUpgradeable
      /**
       *Submitted for verification at Etherscan.io on 2022-06-08
      */
      
      // Sources flattened with hardhat v2.8.2 https://hardhat.org
      
      // File contracts/solidity/interface/INFTXEligibility.sol
      
      // SPDX-License-Identifier: MIT
      
      pragma solidity ^0.8.0;
      
      interface INFTXEligibility {
          // Read functions.
          function name() external pure returns (string memory);
          function finalized() external view returns (bool);
          function targetAsset() external pure returns (address);
          function checkAllEligible(uint256[] calldata tokenIds)
              external
              view
              returns (bool);
          function checkEligible(uint256[] calldata tokenIds)
              external
              view
              returns (bool[] memory);
          function checkAllIneligible(uint256[] calldata tokenIds)
              external
              view
              returns (bool);
          function checkIsEligible(uint256 tokenId) external view returns (bool);
      
          // Write functions.
          function __NFTXEligibility_init_bytes(bytes calldata configData) external;
          function beforeMintHook(uint256[] calldata tokenIds) external;
          function afterMintHook(uint256[] calldata tokenIds) external;
          function beforeRedeemHook(uint256[] calldata tokenIds) external;
          function afterRedeemHook(uint256[] calldata tokenIds) external;
      }
      
      
      // File contracts/solidity/token/IERC20Upgradeable.sol
      
      
      
      pragma solidity ^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);
      }
      
      
      // File contracts/solidity/proxy/IBeacon.sol
      
      
      
      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 childImplementation() external view returns (address);
          function upgradeChildTo(address newImplementation) external;
      }
      
      
      // File contracts/solidity/interface/INFTXVaultFactory.sol
      
      
      
      pragma solidity ^0.8.0;
      
      interface INFTXVaultFactory is IBeacon {
        // Read functions.
        function numVaults() external view returns (uint256);
        function zapContract() external view returns (address);
        function feeDistributor() external view returns (address);
        function eligibilityManager() external view returns (address);
        function vault(uint256 vaultId) external view returns (address);
        function allVaults() external view returns (address[] memory);
        function vaultsForAsset(address asset) external view returns (address[] memory);
        function isLocked(uint256 id) external view returns (bool);
        function excludedFromFees(address addr) external view returns (bool);
        function factoryMintFee() external view returns (uint64);
        function factoryRandomRedeemFee() external view returns (uint64);
        function factoryTargetRedeemFee() external view returns (uint64);
        function factoryRandomSwapFee() external view returns (uint64);
        function factoryTargetSwapFee() external view returns (uint64);
        function vaultFees(uint256 vaultId) external view returns (uint256, uint256, uint256, uint256, uint256);
      
        event NewFeeDistributor(address oldDistributor, address newDistributor);
        event NewZapContract(address oldZap, address newZap);
        event FeeExclusion(address feeExcluded, bool excluded);
        event NewEligibilityManager(address oldEligManager, address newEligManager);
        event NewVault(uint256 indexed vaultId, address vaultAddress, address assetAddress);
        event UpdateVaultFees(uint256 vaultId, uint256 mintFee, uint256 randomRedeemFee, uint256 targetRedeemFee, uint256 randomSwapFee, uint256 targetSwapFee);
        event DisableVaultFees(uint256 vaultId);
        event UpdateFactoryFees(uint256 mintFee, uint256 randomRedeemFee, uint256 targetRedeemFee, uint256 randomSwapFee, uint256 targetSwapFee);
      
        // Write functions.
        function __NFTXVaultFactory_init(address _vaultImpl, address _feeDistributor) external;
        function createVault(
            string calldata name,
            string calldata symbol,
            address _assetAddress,
            bool is1155,
            bool allowAllItems
        ) external returns (uint256);
        function setFeeDistributor(address _feeDistributor) external;
        function setEligibilityManager(address _eligibilityManager) external;
        function setZapContract(address _zapContract) external;
        function setFeeExclusion(address _excludedAddr, bool excluded) external;
      
        function setFactoryFees(
          uint256 mintFee, 
          uint256 randomRedeemFee, 
          uint256 targetRedeemFee,
          uint256 randomSwapFee, 
          uint256 targetSwapFee
        ) external; 
        function setVaultFees(
            uint256 vaultId, 
            uint256 mintFee, 
            uint256 randomRedeemFee, 
            uint256 targetRedeemFee,
            uint256 randomSwapFee, 
            uint256 targetSwapFee
        ) external;
        function disableVaultFees(uint256 vaultId) external;
      }
      
      
      // File contracts/solidity/interface/INFTXVault.sol
      
      
      
      pragma solidity ^0.8.0;
      
      
      
      interface INFTXVault is IERC20Upgradeable {
          function manager() external view returns (address);
          function assetAddress() external view returns (address);
          function vaultFactory() external view returns (INFTXVaultFactory);
          function eligibilityStorage() external view returns (INFTXEligibility);
      
          function is1155() external view returns (bool);
          function allowAllItems() external view returns (bool);
          function enableMint() external view returns (bool);
          function enableRandomRedeem() external view returns (bool);
          function enableTargetRedeem() external view returns (bool);
          function enableRandomSwap() external view returns (bool);
          function enableTargetSwap() external view returns (bool);
      
          function vaultId() external view returns (uint256);
          function nftIdAt(uint256 holdingsIndex) external view returns (uint256);
          function allHoldings() external view returns (uint256[] memory);
          function totalHoldings() external view returns (uint256);
          function mintFee() external view returns (uint256);
          function randomRedeemFee() external view returns (uint256);
          function targetRedeemFee() external view returns (uint256);
          function randomSwapFee() external view returns (uint256);
          function targetSwapFee() external view returns (uint256);
          function vaultFees() external view returns (uint256, uint256, uint256, uint256, uint256);
      
          event VaultInit(
              uint256 indexed vaultId,
              address assetAddress,
              bool is1155,
              bool allowAllItems
          );
      
          event ManagerSet(address manager);
          event EligibilityDeployed(uint256 moduleIndex, address eligibilityAddr);
          // event CustomEligibilityDeployed(address eligibilityAddr);
      
          event EnableMintUpdated(bool enabled);
          event EnableRandomRedeemUpdated(bool enabled);
          event EnableTargetRedeemUpdated(bool enabled);
          event EnableRandomSwapUpdated(bool enabled);
          event EnableTargetSwapUpdated(bool enabled);
      
          event Minted(uint256[] nftIds, uint256[] amounts, address to);
          event Redeemed(uint256[] nftIds, uint256[] specificIds, address to);
          event Swapped(
              uint256[] nftIds,
              uint256[] amounts,
              uint256[] specificIds,
              uint256[] redeemedIds,
              address to
          );
      
          function __NFTXVault_init(
              string calldata _name,
              string calldata _symbol,
              address _assetAddress,
              bool _is1155,
              bool _allowAllItems
          ) external;
      
          function finalizeVault() external;
      
          function setVaultMetadata(
              string memory name_, 
              string memory symbol_
          ) external;
      
          function setVaultFeatures(
              bool _enableMint,
              bool _enableRandomRedeem,
              bool _enableTargetRedeem,
              bool _enableRandomSwap,
              bool _enableTargetSwap
          ) external;
      
          function setFees(
              uint256 _mintFee,
              uint256 _randomRedeemFee,
              uint256 _targetRedeemFee,
              uint256 _randomSwapFee,
              uint256 _targetSwapFee
          ) external;
          function disableVaultFees() external;
      
          // This function allows for an easy setup of any eligibility module contract from the EligibilityManager.
          // It takes in ABI encoded parameters for the desired module. This is to make sure they can all follow
          // a similar interface.
          function deployEligibilityStorage(
              uint256 moduleIndex,
              bytes calldata initData
          ) external returns (address);
      
          // The manager has control over options like fees and features
          function setManager(address _manager) external;
      
          function mint(
              uint256[] calldata tokenIds,
              uint256[] calldata amounts /* ignored for ERC721 vaults */
          ) external returns (uint256);
      
          function mintTo(
              uint256[] calldata tokenIds,
              uint256[] calldata amounts, /* ignored for ERC721 vaults */
              address to
          ) external returns (uint256);
      
          function redeem(uint256 amount, uint256[] calldata specificIds)
              external
              returns (uint256[] calldata);
      
          function redeemTo(
              uint256 amount,
              uint256[] calldata specificIds,
              address to
          ) external returns (uint256[] calldata);
      
          function swap(
              uint256[] calldata tokenIds,
              uint256[] calldata amounts, /* ignored for ERC721 vaults */
              uint256[] calldata specificIds
          ) external returns (uint256[] calldata);
      
          function swapTo(
              uint256[] calldata tokenIds,
              uint256[] calldata amounts, /* ignored for ERC721 vaults */
              uint256[] calldata specificIds,
              address to
          ) external returns (uint256[] calldata);
      
          function allValidNFTs(uint256[] calldata tokenIds)
              external
              view
              returns (bool);
      }
      
      
      // File contracts/solidity/interface/INFTXEligibilityManager.sol
      
      
      
      pragma solidity ^0.8.0;
      
      interface INFTXEligibilityManager {
          function nftxVaultFactory() external returns (address);
          function eligibilityImpl() external returns (address);
      
          function deployEligibility(uint256 vaultId, bytes calldata initData)
              external
              returns (address);
      }
      
      
      // File contracts/solidity/interface/INFTXFeeDistributor.sol
      
      
      
      pragma solidity ^0.8.0;
      
      interface INFTXFeeDistributor {
        
        struct FeeReceiver {
          uint256 allocPoint;
          address receiver;
          bool isContract;
        }
      
        function nftxVaultFactory() external returns (address);
        function lpStaking() external returns (address);
        function treasury() external returns (address);
        function defaultTreasuryAlloc() external returns (uint256);
        function defaultLPAlloc() external returns (uint256);
        function allocTotal(uint256 vaultId) external returns (uint256);
        function specificTreasuryAlloc(uint256 vaultId) external returns (uint256);
      
        // Write functions.
        function __FeeDistributor__init__(address _lpStaking, address _treasury) external;
        function rescueTokens(address token) external;
        function distribute(uint256 vaultId) external;
        function addReceiver(uint256 _vaultId, uint256 _allocPoint, address _receiver, bool _isContract) external;
        function initializeVaultReceivers(uint256 _vaultId) external;
        function changeMultipleReceiverAlloc(
          uint256[] memory _vaultIds, 
          uint256[] memory _receiverIdxs, 
          uint256[] memory allocPoints
        ) external;
      
        function changeMultipleReceiverAddress(
          uint256[] memory _vaultIds, 
          uint256[] memory _receiverIdxs, 
          address[] memory addresses, 
          bool[] memory isContracts
        ) external;
        function changeReceiverAlloc(uint256 _vaultId, uint256 _idx, uint256 _allocPoint) external;
        function changeReceiverAddress(uint256 _vaultId, uint256 _idx, address _address, bool _isContract) external;
        function removeReceiver(uint256 _vaultId, uint256 _receiverIdx) external;
      
        // Configuration functions.
        function setTreasuryAddress(address _treasury) external;
        function setDefaultTreasuryAlloc(uint256 _allocPoint) external;
        function setSpecificTreasuryAlloc(uint256 _vaultId, uint256 _allocPoint) external;
        function setLPStakingAddress(address _lpStaking) external;
        function setNFTXVaultFactory(address _factory) external;
        function setDefaultLPAlloc(uint256 _allocPoint) external;
      }
      
      
      // File contracts/solidity/interface/IERC3156Upgradeable.sol
      
      
      
      pragma solidity ^0.8.0;
      
      /**
       * @dev Interface of the ERC3156 FlashBorrower, as defined in
       * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
       */
      interface IERC3156FlashBorrowerUpgradeable {
          /**
           * @dev Receive a flash loan.
           * @param initiator The initiator of the loan.
           * @param token The loan currency.
           * @param amount The amount of tokens lent.
           * @param fee The additional amount of tokens to repay.
           * @param data Arbitrary data structure, intended to contain user-defined parameters.
           * @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan"
           */
          function onFlashLoan(
              address initiator,
              address token,
              uint256 amount,
              uint256 fee,
              bytes calldata data
          ) external returns (bytes32);
      }
      
      /**
       * @dev Interface of the ERC3156 FlashLender, as defined in
       * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
       */
      interface IERC3156FlashLenderUpgradeable {
          /**
           * @dev The amount of currency available to be lended.
           * @param token The loan currency.
           * @return The amount of `token` that can be borrowed.
           */
          function maxFlashLoan(
              address token
          ) external view returns (uint256);
      
          /**
           * @dev The fee to be charged for a given loan.
           * @param token The loan currency.
           * @param amount The amount of tokens lent.
           * @return The amount of `token` to be charged for the loan, on top of the returned principal.
           */
          function flashFee(
              address token,
              uint256 amount
          ) external view returns (uint256);
      
          /**
           * @dev Initiate a flash loan.
           * @param receiver The receiver of the tokens in the loan, and the receiver of the callback.
           * @param token The loan currency.
           * @param amount The amount of tokens lent.
           * @param data Arbitrary data structure, intended to contain user-defined parameters.
           */
          function flashLoan(
              IERC3156FlashBorrowerUpgradeable receiver,
              address token,
              uint256 amount,
              bytes calldata data
          ) external returns (bool);
       }
      
      
      // File contracts/solidity/token/IERC20Metadata.sol
      
      
      
      pragma solidity ^0.8.0;
      
      /**
       * @dev Interface for the optional metadata functions from the ERC20 standard.
       *
       * _Available since v4.1._
       */
      interface IERC20Metadata is IERC20Upgradeable {
          /**
           * @dev Returns the name of the token.
           */
          function name() external view returns (string memory);
      
          /**
           * @dev Returns the symbol of the token.
           */
          function symbol() external view returns (string memory);
      
          /**
           * @dev Returns the decimals places of the token.
           */
          function decimals() external view returns (uint8);
      }
      
      
      // File contracts/solidity/proxy/Initializable.sol
      
      
      
      // solhint-disable-next-line compiler-version
      pragma solidity ^0.8.0;
      
      /**
       * @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 {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.
       */
      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 || !_initialized, "Initializable: contract is already initialized");
      
              bool isTopLevelCall = !_initializing;
              if (isTopLevelCall) {
                  _initializing = true;
                  _initialized = true;
              }
      
              _;
      
              if (isTopLevelCall) {
                  _initializing = false;
              }
          }
      }
      
      
      // File contracts/solidity/util/ContextUpgradeable.sol
      
      
      
      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 ContextUpgradeable is Initializable {
          function __Context_init() internal initializer {
              __Context_init_unchained();
          }
      
          function __Context_init_unchained() internal initializer {
          }
          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;
          }
          uint256[50] private __gap;
      }
      
      
      // File contracts/solidity/token/ERC20Upgradeable.sol
      
      
      
      pragma solidity ^0.8.0;
      
      
      
      
      /**
       * @dev Implementation of the {IERC20} interface.
       *
       * This implementation is agnostic to the way tokens are created. This means
       * that a supply mechanism has to be added in a derived contract using {_mint}.
       * For a generic mechanism see {ERC20PresetMinterPauser}.
       *
       * TIP: For a detailed writeup see our guide
       * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
       * to implement supply mechanisms].
       *
       * We have followed general OpenZeppelin guidelines: functions revert instead
       * of returning `false` on failure. This behavior is nonetheless conventional
       * and does not conflict with the expectations of ERC20 applications.
       *
       * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
       * This allows applications to reconstruct the allowance for all accounts just
       * by listening to said events. Other implementations of the EIP may not emit
       * these events, as it isn't required by the specification.
       *
       * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
       * functions have been added to mitigate the well-known issues around setting
       * allowances. See {IERC20-approve}.
       */
      contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20Metadata {
          mapping (address => uint256) private _balances;
      
          mapping (address => mapping (address => uint256)) private _allowances;
      
          uint256 private _totalSupply;
      
          string private _name;
          string private _symbol;
      
          /**
           * @dev Sets the values for {name} and {symbol}.
           *
           * The default value of {decimals} is 18. To select a different value for
           * {decimals} you should overload it.
           *
           * All two of these values are immutable: they can only be set once during
           * construction.
           */
          function __ERC20_init(string memory name_, string memory symbol_) internal initializer {
              __Context_init_unchained();
              __ERC20_init_unchained(name_, symbol_);
          }
      
          function __ERC20_init_unchained(string memory name_, string memory symbol_) internal initializer {
              _name = name_;
              _symbol = symbol_;
          }
      
          function _setMetadata(string memory name_, string memory symbol_) internal {
              _name = name_;
              _symbol = symbol_;
          }
      
          /**
           * @dev Returns the name of the token.
           */
          function name() public view virtual override returns (string memory) {
              return _name;
          }
      
          /**
           * @dev Returns the symbol of the token, usually a shorter version of the
           * name.
           */
          function symbol() public view virtual override returns (string memory) {
              return _symbol;
          }
      
          /**
           * @dev Returns the number of decimals used to get its user representation.
           * For example, if `decimals` equals `2`, a balance of `505` tokens should
           * be displayed to a user as `5,05` (`505 / 10 ** 2`).
           *
           * Tokens usually opt for a value of 18, imitating the relationship between
           * Ether and Wei. This is the value {ERC20} uses, unless this function is
           * overridden;
           *
           * NOTE: This information is only used for _display_ purposes: it in
           * no way affects any of the arithmetic of the contract, including
           * {IERC20-balanceOf} and {IERC20-transfer}.
           */
          function decimals() public view virtual override returns (uint8) {
              return 18;
          }
      
          /**
           * @dev See {IERC20-totalSupply}.
           */
          function totalSupply() public view virtual override returns (uint256) {
              return _totalSupply;
          }
      
          /**
           * @dev See {IERC20-balanceOf}.
           */
          function balanceOf(address account) public view virtual override returns (uint256) {
              return _balances[account];
          }
      
          /**
           * @dev See {IERC20-transfer}.
           *
           * Requirements:
           *
           * - `recipient` cannot be the zero address.
           * - the caller must have a balance of at least `amount`.
           */
          function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
              _transfer(_msgSender(), recipient, amount);
              return true;
          }
      
          /**
           * @dev See {IERC20-allowance}.
           */
          function allowance(address owner, address spender) public view virtual override returns (uint256) {
              return _allowances[owner][spender];
          }
      
          /**
           * @dev See {IERC20-approve}.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          function approve(address spender, uint256 amount) public virtual override returns (bool) {
              _approve(_msgSender(), spender, amount);
              return true;
          }
      
          /**
           * @dev See {IERC20-transferFrom}.
           *
           * Emits an {Approval} event indicating the updated allowance. This is not
           * required by the EIP. See the note at the beginning of {ERC20}.
           *
           * Requirements:
           *
           * - `sender` and `recipient` cannot be the zero address.
           * - `sender` must have a balance of at least `amount`.
           * - the caller must have allowance for ``sender``'s tokens of at least
           * `amount`.
           */
          function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
              _transfer(sender, recipient, amount);
      
              uint256 currentAllowance = _allowances[sender][_msgSender()];
              require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
              _approve(sender, _msgSender(), currentAllowance - amount);
      
              return true;
          }
      
          /**
           * @dev Atomically increases the allowance granted to `spender` by the caller.
           *
           * This is an alternative to {approve} that can be used as a mitigation for
           * problems described in {IERC20-approve}.
           *
           * Emits an {Approval} event indicating the updated allowance.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
              _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
              return true;
          }
      
          /**
           * @dev Atomically decreases the allowance granted to `spender` by the caller.
           *
           * This is an alternative to {approve} that can be used as a mitigation for
           * problems described in {IERC20-approve}.
           *
           * Emits an {Approval} event indicating the updated allowance.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           * - `spender` must have allowance for the caller of at least
           * `subtractedValue`.
           */
          function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
              uint256 currentAllowance = _allowances[_msgSender()][spender];
              require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
              _approve(_msgSender(), spender, currentAllowance - subtractedValue);
      
              return true;
          }
      
          /**
           * @dev Moves tokens `amount` from `sender` to `recipient`.
           *
           * This is internal function is equivalent to {transfer}, and can be used to
           * e.g. implement automatic token fees, slashing mechanisms, etc.
           *
           * Emits a {Transfer} event.
           *
           * Requirements:
           *
           * - `sender` cannot be the zero address.
           * - `recipient` cannot be the zero address.
           * - `sender` must have a balance of at least `amount`.
           */
          function _transfer(address sender, address recipient, uint256 amount) internal virtual {
              require(sender != address(0), "ERC20: transfer from the zero address");
              require(recipient != address(0), "ERC20: transfer to the zero address");
      
              _beforeTokenTransfer(sender, recipient, amount);
      
              uint256 senderBalance = _balances[sender];
              require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
              _balances[sender] = senderBalance - amount;
              _balances[recipient] += amount;
      
              emit Transfer(sender, recipient, amount);
          }
      
          /** @dev Creates `amount` tokens and assigns them to `account`, increasing
           * the total supply.
           *
           * Emits a {Transfer} event with `from` set to the zero address.
           *
           * Requirements:
           *
           * - `account` cannot be the zero address.
           */
          function _mint(address account, uint256 amount) internal virtual {
              require(account != address(0), "ERC20: mint to the zero address");
      
              _beforeTokenTransfer(address(0), account, amount);
      
              _totalSupply += amount;
              _balances[account] += amount;
              emit Transfer(address(0), account, amount);
          }
      
          /**
           * @dev Destroys `amount` tokens from `account`, reducing the
           * total supply.
           *
           * Emits a {Transfer} event with `to` set to the zero address.
           *
           * Requirements:
           *
           * - `account` cannot be the zero address.
           * - `account` must have at least `amount` tokens.
           */
          function _burn(address account, uint256 amount) internal virtual {
              require(account != address(0), "ERC20: burn from the zero address");
      
              _beforeTokenTransfer(account, address(0), amount);
      
              uint256 accountBalance = _balances[account];
              require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
              _balances[account] = accountBalance - amount;
              _totalSupply -= amount;
      
              emit Transfer(account, address(0), amount);
          }
      
          /**
           * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
           *
           * This internal function is equivalent to `approve`, and can be used to
           * e.g. set automatic allowances for certain subsystems, etc.
           *
           * Emits an {Approval} event.
           *
           * Requirements:
           *
           * - `owner` cannot be the zero address.
           * - `spender` cannot be the zero address.
           */
          function _approve(address owner, address spender, uint256 amount) internal virtual {
              require(owner != address(0), "ERC20: approve from the zero address");
              require(spender != address(0), "ERC20: approve to the zero address");
      
              _allowances[owner][spender] = amount;
              emit Approval(owner, spender, amount);
          }
      
          /**
           * @dev Hook that is called before any transfer of tokens. This includes
           * minting and burning.
           *
           * Calling conditions:
           *
           * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
           * will be to transferred to `to`.
           * - when `from` is zero, `amount` tokens will be minted for `to`.
           * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
           * - `from` and `to` are never both zero.
           *
           * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
           */
          function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
          uint256[45] private __gap;
      }
      
      
      // File contracts/solidity/token/ERC20FlashMintUpgradeable.sol
      
      
      
      pragma solidity ^0.8.0;
      
      
      
      /**
       * @dev Implementation of the ERC3156 Flash loans extension, as defined in
       * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
       *
       * Adds the {flashLoan} method, which provides flash loan support at the token
       * level. By default there is no fee, but this can be changed by overriding {flashFee}.
       */
      abstract contract ERC20FlashMintUpgradeable is Initializable, ERC20Upgradeable, IERC3156FlashLenderUpgradeable {
          function __ERC20FlashMint_init() internal initializer {
              __Context_init_unchained();
              __ERC20FlashMint_init_unchained();
          }
      
          function __ERC20FlashMint_init_unchained() internal initializer {
          }
          bytes32 constant private RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan");
      
          /**
           * @dev Returns the maximum amount of tokens available for loan.
           * @param token The address of the token that is requested.
           * @return The amont of token that can be loaned.
           */
          function maxFlashLoan(address token) public view override returns (uint256) {
              return token == address(this) ? type(uint256).max - totalSupply() : 0;
          }
      
          /**
           * @dev Returns the fee applied when doing flash loans. By default this
           * implementation has 0 fees. This function can be overloaded to make
           * the flash loan mechanism deflationary.
           * @param token The token to be flash loaned.
           * @param amount The amount of tokens to be loaned.
           * @return The fees applied to the corresponding flash loan.
           */
          function flashFee(address token, uint256 amount) public view virtual override returns (uint256) {
              require(token == address(this), "ERC20FlashMint: wrong token");
              // silence warning about unused variable without the addition of bytecode.
              amount;
              return 0;
          }
      
          /**
           * @dev Performs a flash loan. New tokens are minted and sent to the
           * `receiver`, who is required to implement the {IERC3156FlashBorrower}
           * interface. By the end of the flash loan, the receiver is expected to own
           * amount + fee tokens and have them approved back to the token contract itself so
           * they can be burned.
           * @param receiver The receiver of the flash loan. Should implement the
           * {IERC3156FlashBorrower.onFlashLoan} interface.
           * @param token The token to be flash loaned. Only `address(this)` is
           * supported.
           * @param amount The amount of tokens to be loaned.
           * @param data An arbitrary datafield that is passed to the receiver.
           * @return `true` is the flash loan was successfull.
           */
          function flashLoan(
              IERC3156FlashBorrowerUpgradeable receiver,
              address token,
              uint256 amount,
              bytes memory data
          )
              public virtual override returns (bool)
          {
              uint256 fee = flashFee(token, amount);
              _mint(address(receiver), amount);
              require(receiver.onFlashLoan(msg.sender, token, amount, fee, data) == RETURN_VALUE, "ERC20FlashMint: invalid return value");
              uint256 currentAllowance = allowance(address(receiver), address(this));
              require(currentAllowance >= amount + fee, "ERC20FlashMint: allowance does not allow refund");
              _approve(address(receiver), address(this), currentAllowance - amount - fee);
              _burn(address(receiver), amount + fee);
              return true;
          }
          uint256[50] private __gap;
      }
      
      
      // File contracts/solidity/token/IERC721ReceiverUpgradeable.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 `IERC721.onERC721Received.selector`.
           */
          function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);
      }
      
      
      // File contracts/solidity/token/ERC721SafeHolderUpgradeable.sol
      
      
      
      pragma solidity ^0.8.0;
      
      /**
       * @dev Implementation of the {IERC721Receiver} interface.
       *
       * Accepts all token transfers.
       * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}.
       */
      contract ERC721SafeHolderUpgradeable is IERC721ReceiverUpgradeable {
          /**
           * @dev See {IERC721Receiver-onERC721Received}.
           *
           * Always returns `IERC721Receiver.onERC721Received.selector`.
           */
          function onERC721Received(
              address operator,
              address,
              uint256,
              bytes memory
          ) public virtual override returns (bytes4) {
              return this.onERC721Received.selector;
          }
      }
      
      
      // File contracts/solidity/interface/IERC165Upgradeable.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);
      }
      
      
      // File contracts/solidity/token/IERC1155ReceiverUpgradeable.sol
      
      
      
      pragma solidity ^0.8.0;
      
      /**
       * @dev _Available since v3.1._
       */
      interface IERC1155ReceiverUpgradeable is IERC165Upgradeable {
      
          /**
              @dev Handles the receipt of a single ERC1155 token type. This function is
              called at the end of a `safeTransferFrom` after the balance has been updated.
              To accept the transfer, this must return
              `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
              (i.e. 0xf23a6e61, or its own function selector).
              @param operator The address which initiated the transfer (i.e. msg.sender)
              @param from The address which previously owned the token
              @param id The ID of the token being transferred
              @param value The amount of tokens being transferred
              @param data Additional data with no specified format
              @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
          */
          function onERC1155Received(
              address operator,
              address from,
              uint256 id,
              uint256 value,
              bytes calldata data
          )
              external
              returns(bytes4);
      
          /**
              @dev Handles the receipt of a multiple ERC1155 token types. This function
              is called at the end of a `safeBatchTransferFrom` after the balances have
              been updated. To accept the transfer(s), this must return
              `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
              (i.e. 0xbc197c81, or its own function selector).
              @param operator The address which initiated the batch transfer (i.e. msg.sender)
              @param from The address which previously owned the token
              @param ids An array containing ids of each token being transferred (order and length must match values array)
              @param values An array containing amounts of each token being transferred (order and length must match ids array)
              @param data Additional data with no specified format
              @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
          */
          function onERC1155BatchReceived(
              address operator,
              address from,
              uint256[] calldata ids,
              uint256[] calldata values,
              bytes calldata data
          )
              external
              returns(bytes4);
      }
      
      
      // File contracts/solidity/util/ERC165Upgradeable.sol
      
      
      
      pragma solidity ^0.8.0;
      
      /**
       * @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 IERC165Upgradeable {
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
              return interfaceId == type(IERC165Upgradeable).interfaceId;
          }
      }
      
      
      // File contracts/solidity/token/ERC1155ReceiverUpgradeable.sol
      
      
      
      pragma solidity ^0.8.0;
      
      
      /**
       * @dev _Available since v3.1._
       */
      abstract contract ERC1155ReceiverUpgradeable is ERC165Upgradeable, IERC1155ReceiverUpgradeable {
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) {
              return interfaceId == type(IERC1155ReceiverUpgradeable).interfaceId
                  || super.supportsInterface(interfaceId);
          }
      }
      
      
      // File contracts/solidity/token/ERC1155SafeHolderUpgradeable.sol
      
      
      
      pragma solidity ^0.8.0;
      
      /**
       * @dev _Available since v3.1._
       */
      abstract contract ERC1155SafeHolderUpgradeable is ERC1155ReceiverUpgradeable {
          function onERC1155Received(address operator, address, uint256, uint256, bytes memory) public virtual override returns (bytes4) {
              return this.onERC1155Received.selector;
          }
      
          function onERC1155BatchReceived(address operator, address, uint256[] memory, uint256[] memory, bytes memory) public virtual override returns (bytes4) {
              return this.onERC1155BatchReceived.selector;
          }
      }
      
      
      // File contracts/solidity/token/IERC721Upgradeable.sol
      
      
      
      pragma solidity ^0.8.0;
      
      /**
       * @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;
      }
      
      
      // File contracts/solidity/token/IERC1155Upgradeable.sol
      
      
      
      pragma solidity ^0.8.0;
      
      /**
       * @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;
      }
      
      
      // File contracts/solidity/util/OwnableUpgradeable.sol
      
      
      
      pragma solidity ^0.8.0;
      
      
      /**
       * @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;
      }
      
      
      // File contracts/solidity/util/ReentrancyGuardUpgradeable.sol
      
      
      
      pragma solidity ^0.8.0;
      
      /**
       * @dev Contract module that helps prevent reentrant calls to a function.
       *
       * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
       * available, which can be applied to functions to make sure there are no nested
       * (reentrant) calls to them.
       *
       * Note that because there is a single `nonReentrant` guard, functions marked as
       * `nonReentrant` may not call one another. This can be worked around by making
       * those functions `private`, and then adding `external` `nonReentrant` entry
       * points to them.
       *
       * TIP: If you would like to learn more about reentrancy and alternative ways
       * to protect against it, check out our blog post
       * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
       */
      abstract contract ReentrancyGuardUpgradeable is Initializable {
          // Booleans are more expensive than uint256 or any type that takes up a full
          // word because each write operation emits an extra SLOAD to first read the
          // slot's contents, replace the bits taken up by the boolean, and then write
          // back. This is the compiler's defense against contract upgrades and
          // pointer aliasing, and it cannot be disabled.
      
          // The values being non-zero value makes deployment a bit more expensive,
          // but in exchange the refund on every call to nonReentrant will be lower in
          // amount. Since refunds are capped to a percentage of the total
          // transaction's gas, it is best to keep them low in cases like this one, to
          // increase the likelihood of the full refund coming into effect.
          uint256 private constant _NOT_ENTERED = 1;
          uint256 private constant _ENTERED = 2;
      
          uint256 private _status;
      
          function __ReentrancyGuard_init() internal initializer {
              __ReentrancyGuard_init_unchained();
          }
      
          function __ReentrancyGuard_init_unchained() internal initializer {
              _status = _NOT_ENTERED;
          }
      
          /**
           * @dev Prevents a contract from calling itself, directly or indirectly.
           * Calling a `nonReentrant` function from another `nonReentrant`
           * function is not supported. It is possible to prevent this from happening
           * by making the `nonReentrant` function external, and make it call a
           * `private` function that does the actual work.
           */
          modifier nonReentrant() {
              // On the first call to nonReentrant, _notEntered will be true
              require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
      
              // Any calls to nonReentrant after this point will fail
              _status = _ENTERED;
      
              _;
      
              // By storing the original value once again, a refund is triggered (see
              // https://eips.ethereum.org/EIPS/eip-2200)
              _status = _NOT_ENTERED;
          }
          uint256[49] private __gap;
      }
      
      
      // File contracts/solidity/util/EnumerableSetUpgradeable.sol
      
      
      
      pragma solidity ^0.8.0;
      
      /**
       * @dev Library for managing
       * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
       * types.
       *
       * Sets have the following properties:
       *
       * - Elements are added, removed, and checked for existence in constant time
       * (O(1)).
       * - Elements are enumerated in O(n). No guarantees are made on the ordering.
       *
       * ```
       * contract Example {
       *     // Add the library methods
       *     using EnumerableSet for EnumerableSet.AddressSet;
       *
       *     // Declare a set state variable
       *     EnumerableSet.AddressSet private mySet;
       * }
       * ```
       *
       * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
       * and `uint256` (`UintSet`) are supported.
       */
      library EnumerableSetUpgradeable {
          // To implement this library for multiple types with as little code
          // repetition as possible, we write it in terms of a generic Set type with
          // bytes32 values.
          // The Set implementation uses private functions, and user-facing
          // implementations (such as AddressSet) are just wrappers around the
          // underlying Set.
          // This means that we can only create new EnumerableSets for types that fit
          // in bytes32.
      
          struct Set {
              // Storage of set values
              bytes32[] _values;
      
              // Position of the value in the `values` array, plus 1 because index 0
              // means a value is not in the set.
              mapping (bytes32 => uint256) _indexes;
          }
      
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function _add(Set storage set, bytes32 value) private returns (bool) {
              if (!_contains(set, value)) {
                  set._values.push(value);
                  // The value is stored at length-1, but we add 1 to all indexes
                  // and use 0 as a sentinel value
                  set._indexes[value] = set._values.length;
                  return true;
              } else {
                  return false;
              }
          }
      
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function _remove(Set storage set, bytes32 value) private returns (bool) {
              // We read and store the value's index to prevent multiple reads from the same storage slot
              uint256 valueIndex = set._indexes[value];
      
              if (valueIndex != 0) { // Equivalent to contains(set, value)
                  // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                  // the array, and then remove the last element (sometimes called as 'swap and pop').
                  // This modifies the order of the array, as noted in {at}.
      
                  uint256 toDeleteIndex = valueIndex - 1;
                  uint256 lastIndex = set._values.length - 1;
      
                  if (lastIndex != toDeleteIndex) {
                      bytes32 lastvalue = set._values[lastIndex];
      
                      // Move the last value to the index where the value to delete is
                      set._values[toDeleteIndex] = lastvalue;
                      // Update the index for the moved value
                      set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex
                  }
      
                  // Delete the slot where the moved value was stored
                  set._values.pop();
      
                  // Delete the index for the deleted slot
                  delete set._indexes[value];
      
                  return true;
              } else {
                  return false;
              }
          }
      
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function _contains(Set storage set, bytes32 value) private view returns (bool) {
              return set._indexes[value] != 0;
          }
      
          /**
           * @dev Returns the number of values on the set. O(1).
           */
          function _length(Set storage set) private view returns (uint256) {
              return set._values.length;
          }
      
         /**
          * @dev Returns the value stored at position `index` in the set. O(1).
          *
          * Note that there are no guarantees on the ordering of values inside the
          * array, and it may change when more values are added or removed.
          *
          * Requirements:
          *
          * - `index` must be strictly less than {length}.
          */
          function _at(Set storage set, uint256 index) private view returns (bytes32) {
              return set._values[index];
          }
      
          // Bytes32Set
      
          struct Bytes32Set {
              Set _inner;
          }
      
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
              return _add(set._inner, value);
          }
      
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
              return _remove(set._inner, value);
          }
      
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
              return _contains(set._inner, value);
          }
      
          /**
           * @dev Returns the number of values in the set. O(1).
           */
          function length(Bytes32Set storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
      
         /**
          * @dev Returns the value stored at position `index` in the set. O(1).
          *
          * Note that there are no guarantees on the ordering of values inside the
          * array, and it may change when more values are added or removed.
          *
          * Requirements:
          *
          * - `index` must be strictly less than {length}.
          */
          function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
              return _at(set._inner, index);
          }
      
          // AddressSet
      
          struct AddressSet {
              Set _inner;
          }
      
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(AddressSet storage set, address value) internal returns (bool) {
              return _add(set._inner, bytes32(uint256(uint160(value))));
          }
      
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(AddressSet storage set, address value) internal returns (bool) {
              return _remove(set._inner, bytes32(uint256(uint160(value))));
          }
      
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(AddressSet storage set, address value) internal view returns (bool) {
              return _contains(set._inner, bytes32(uint256(uint160(value))));
          }
      
          /**
           * @dev Returns the number of values in the set. O(1).
           */
          function length(AddressSet storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
      
         /**
          * @dev Returns the value stored at position `index` in the set. O(1).
          *
          * Note that there are no guarantees on the ordering of values inside the
          * array, and it may change when more values are added or removed.
          *
          * Requirements:
          *
          * - `index` must be strictly less than {length}.
          */
          function at(AddressSet storage set, uint256 index) internal view returns (address) {
              return address(uint160(uint256(_at(set._inner, index))));
          }
      
      
          // UintSet
      
          struct UintSet {
              Set _inner;
          }
      
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(UintSet storage set, uint256 value) internal returns (bool) {
              return _add(set._inner, bytes32(value));
          }
      
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(UintSet storage set, uint256 value) internal returns (bool) {
              return _remove(set._inner, bytes32(value));
          }
      
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(UintSet storage set, uint256 value) internal view returns (bool) {
              return _contains(set._inner, bytes32(value));
          }
      
          /**
           * @dev Returns the number of values on the set. O(1).
           */
          function length(UintSet storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
      
         /**
          * @dev Returns the value stored at position `index` in the set. O(1).
          *
          * Note that there are no guarantees on the ordering of values inside the
          * array, and it may change when more values are added or removed.
          *
          * Requirements:
          *
          * - `index` must be strictly less than {length}.
          */
          function at(UintSet storage set, uint256 index) internal view returns (uint256) {
              return uint256(_at(set._inner, index));
          }
      }
      
      
      // File contracts/solidity/NFTXVaultUpgradeable.sol
      
      
      
      pragma solidity ^0.8.0;
      
      
      
      
      
      
      
      
      
      
      
      
      
      // Authors: @0xKiwi_ and @alexgausman.
      
      contract NFTXVaultUpgradeable is
          OwnableUpgradeable,
          ERC20FlashMintUpgradeable,
          ReentrancyGuardUpgradeable,
          ERC721SafeHolderUpgradeable,
          ERC1155SafeHolderUpgradeable,
          INFTXVault
      {
          using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;
      
          uint256 constant base = 10**18;
      
          uint256 public override vaultId;
          address public override manager;
          address public override assetAddress;
          INFTXVaultFactory public override vaultFactory;
          INFTXEligibility public override eligibilityStorage;
      
          uint256 randNonce;
          uint256 private UNUSED_FEE1;
          uint256 private UNUSED_FEE2;
          uint256 private UNUSED_FEE3;
      
          bool public override is1155;
          bool public override allowAllItems;
          bool public override enableMint;
          bool public override enableRandomRedeem;
          bool public override enableTargetRedeem;
      
          EnumerableSetUpgradeable.UintSet holdings;
          mapping(uint256 => uint256) quantity1155;
      
          bool public override enableRandomSwap;
          bool public override enableTargetSwap;
      
          event VaultShutdown(address assetAddress, uint256 numItems, address recipient);
          event MetaDataChange(string oldName, string oldSymbol, string newName, string newSymbol);
      
          function __NFTXVault_init(
              string memory _name,
              string memory _symbol,
              address _assetAddress,
              bool _is1155,
              bool _allowAllItems
          ) public override virtual initializer {
              __Ownable_init();
              __ERC20_init(_name, _symbol);
              require(_assetAddress != address(0), "Asset != address(0)");
              assetAddress = _assetAddress;
              vaultFactory = INFTXVaultFactory(msg.sender);
              vaultId = vaultFactory.numVaults();
              is1155 = _is1155;
              allowAllItems = _allowAllItems;
              emit VaultInit(vaultId, _assetAddress, _is1155, _allowAllItems);
              setVaultFeatures(true /*enableMint*/, true /*enableRandomRedeem*/, true /*enableTargetRedeem*/, true /*enableRandomSwap*/, true /*enableTargetSwap*/);
          }
      
          function finalizeVault() external override virtual {
              setManager(address(0));
          }
      
          // Added in v1.0.3.
          function setVaultMetadata(
              string calldata name_, 
              string calldata symbol_
          ) external override virtual {
              onlyPrivileged();
              emit MetaDataChange(name(), symbol(), name_, symbol_);
              _setMetadata(name_, symbol_);
          }
      
          function setVaultFeatures(
              bool _enableMint,
              bool _enableRandomRedeem,
              bool _enableTargetRedeem,
              bool _enableRandomSwap,
              bool _enableTargetSwap
          ) public override virtual {
              onlyPrivileged();
              enableMint = _enableMint;
              enableRandomRedeem = _enableRandomRedeem;
              enableTargetRedeem = _enableTargetRedeem;
              enableRandomSwap = _enableRandomSwap;
              enableTargetSwap = _enableTargetSwap;
      
              emit EnableMintUpdated(_enableMint);
              emit EnableRandomRedeemUpdated(_enableRandomRedeem);
              emit EnableTargetRedeemUpdated(_enableTargetRedeem);
              emit EnableRandomSwapUpdated(_enableRandomSwap);
              emit EnableTargetSwapUpdated(_enableTargetSwap);
          }
      
          function setFees(
              uint256 _mintFee,
              uint256 _randomRedeemFee,
              uint256 _targetRedeemFee,
              uint256 _randomSwapFee,
              uint256 _targetSwapFee
          ) public override virtual {
              onlyPrivileged();
              vaultFactory.setVaultFees(vaultId, _mintFee, _randomRedeemFee, _targetRedeemFee, _randomSwapFee, _targetSwapFee);
          }
      
          function disableVaultFees() public override virtual {
              onlyPrivileged();
              vaultFactory.disableVaultFees(vaultId);
          }
      
          // This function allows for an easy setup of any eligibility module contract from the EligibilityManager.
          // It takes in ABI encoded parameters for the desired module. This is to make sure they can all follow 
          // a similar interface.
          function deployEligibilityStorage(
              uint256 moduleIndex,
              bytes calldata initData
          ) external override virtual returns (address) {
              onlyPrivileged();
              require(
                  address(eligibilityStorage) == address(0),
                  "NFTXVault: eligibility already set"
              );
              INFTXEligibilityManager eligManager = INFTXEligibilityManager(
                  vaultFactory.eligibilityManager()
              );
              address _eligibility = eligManager.deployEligibility(
                  moduleIndex,
                  initData
              );
              eligibilityStorage = INFTXEligibility(_eligibility);
              // Toggle this to let the contract know to check eligibility now.
              allowAllItems = false;
              emit EligibilityDeployed(moduleIndex, _eligibility);
              return _eligibility;
          }
      
          // // This function allows for the manager to set their own arbitrary eligibility contract.
          // // Once eligiblity is set, it cannot be unset or changed.
          // Disabled for launch.
          // function setEligibilityStorage(address _newEligibility) public virtual {
          //     onlyPrivileged();
          //     require(
          //         address(eligibilityStorage) == address(0),
          //         "NFTXVault: eligibility already set"
          //     );
          //     eligibilityStorage = INFTXEligibility(_newEligibility);
          //     // Toggle this to let the contract know to check eligibility now.
          //     allowAllItems = false;
          //     emit CustomEligibilityDeployed(address(_newEligibility));
          // }
      
          // The manager has control over options like fees and features
          function setManager(address _manager) public override virtual {
              onlyPrivileged();
              manager = _manager;
              emit ManagerSet(_manager);
          }
      
          function mint(
              uint256[] calldata tokenIds,
              uint256[] calldata amounts /* ignored for ERC721 vaults */
          ) external override virtual returns (uint256) {
              return mintTo(tokenIds, amounts, msg.sender);
          }
      
          function mintTo(
              uint256[] memory tokenIds,
              uint256[] memory amounts, /* ignored for ERC721 vaults */
              address to
          ) public override virtual nonReentrant returns (uint256) {
              onlyOwnerIfPaused(1);
              checkAddressOnDenyList(msg.sender);
              require(enableMint, "Minting not enabled");
              // Take the NFTs.
              uint256 count = receiveNFTs(tokenIds, amounts);
      
              // Mint to the user.
              _mint(to, base * count);
              uint256 totalFee = mintFee() * count;
              _chargeAndDistributeFees(to, totalFee);
      
              emit Minted(tokenIds, amounts, to);
              return count;
          }
      
          function redeem(uint256 amount, uint256[] calldata specificIds)
              external
              override
              virtual
              returns (uint256[] memory)
          {
              return redeemTo(amount, specificIds, msg.sender);
          }
      
          function redeemTo(uint256 amount, uint256[] memory specificIds, address to)
              public
              override
              virtual
              nonReentrant
              returns (uint256[] memory)
          {
              onlyOwnerIfPaused(2);
              checkAddressOnDenyList(msg.sender);
              require(
                  amount == specificIds.length || enableRandomRedeem,
                  "NFTXVault: Random redeem not enabled"
              );
              require(
                  specificIds.length == 0 || enableTargetRedeem,
                  "NFTXVault: Target redeem not enabled"
              );
              
              // We burn all from sender and mint to fee receiver to reduce costs.
              _burn(msg.sender, base * amount);
      
              // Pay the tokens + toll.
              (, uint256 _randomRedeemFee, uint256 _targetRedeemFee, ,) = vaultFees();
              uint256 totalFee = (_targetRedeemFee * specificIds.length) + (
                  _randomRedeemFee * (amount - specificIds.length)
              );
              _chargeAndDistributeFees(msg.sender, totalFee);
      
              // Withdraw from vault.
              uint256[] memory redeemedIds = withdrawNFTsTo(amount, specificIds, to);
              emit Redeemed(redeemedIds, specificIds, to);
              return redeemedIds;
          }
          
          function swap(
              uint256[] calldata tokenIds,
              uint256[] calldata amounts, /* ignored for ERC721 vaults */
              uint256[] calldata specificIds
          ) external override virtual returns (uint256[] memory) {
              return swapTo(tokenIds, amounts, specificIds, msg.sender);
          }
      
          function swapTo(
              uint256[] memory tokenIds,
              uint256[] memory amounts, /* ignored for ERC721 vaults */
              uint256[] memory specificIds,
              address to
          ) public override virtual nonReentrant returns (uint256[] memory) {
              onlyOwnerIfPaused(3);
              checkAddressOnDenyList(msg.sender);
              uint256 count;
              if (is1155) {
                  for (uint256 i; i < tokenIds.length; ++i) {
                      uint256 amount = amounts[i];
                      require(amount != 0, "NFTXVault: transferring < 1");
                      count += amount;
                  }
              } else {
                  count = tokenIds.length;
              }
      
              require(
                  count == specificIds.length || enableRandomSwap,
                  "NFTXVault: Random swap disabled"
              );
              require(
                  specificIds.length == 0 || enableTargetSwap,
                  "NFTXVault: Target swap disabled"
              );
      
              (, , ,uint256 _randomSwapFee, uint256 _targetSwapFee) = vaultFees();
              uint256 totalFee = (_targetSwapFee * specificIds.length) + (
                  _randomSwapFee * (count - specificIds.length)
              );
              _chargeAndDistributeFees(msg.sender, totalFee);
              
              // Give the NFTs first, so the user wont get the same thing back, just to be nice. 
              uint256[] memory ids = withdrawNFTsTo(count, specificIds, to);
      
              receiveNFTs(tokenIds, amounts);
      
              emit Swapped(tokenIds, amounts, specificIds, ids, to);
              return ids;
          }
      
          function flashLoan(
              IERC3156FlashBorrowerUpgradeable receiver,
              address token,
              uint256 amount,
              bytes memory data
          ) public override virtual returns (bool) {
              onlyOwnerIfPaused(4);
              return super.flashLoan(receiver, token, amount, data);
          }
      
          function mintFee() public view override virtual returns (uint256) {
              (uint256 _mintFee, , , ,) = vaultFactory.vaultFees(vaultId);
              return _mintFee;
          }
      
          function randomRedeemFee() public view override virtual returns (uint256) {
              (, uint256 _randomRedeemFee, , ,) = vaultFactory.vaultFees(vaultId);
              return _randomRedeemFee;
          }
      
          function targetRedeemFee() public view override virtual returns (uint256) {
              (, , uint256 _targetRedeemFee, ,) = vaultFactory.vaultFees(vaultId);
              return _targetRedeemFee;
          }
      
          function randomSwapFee() public view override virtual returns (uint256) {
              (, , , uint256 _randomSwapFee, ) = vaultFactory.vaultFees(vaultId);
              return _randomSwapFee;
          }
      
          function targetSwapFee() public view override virtual returns (uint256) {
              (, , , ,uint256 _targetSwapFee) = vaultFactory.vaultFees(vaultId);
              return _targetSwapFee;
          }
      
          function vaultFees() public view override virtual returns (uint256, uint256, uint256, uint256, uint256) {
              return vaultFactory.vaultFees(vaultId);
          }
      
          function allValidNFTs(uint256[] memory tokenIds)
              public
              view
              override
              virtual
              returns (bool)
          {
              if (allowAllItems) {
                  return true;
              }
      
              INFTXEligibility _eligibilityStorage = eligibilityStorage;
              if (address(_eligibilityStorage) == address(0)) {
                  return false;
              }
              return _eligibilityStorage.checkAllEligible(tokenIds);
          }
      
          function nftIdAt(uint256 holdingsIndex) external view override virtual returns (uint256) {
              return holdings.at(holdingsIndex);
          }
      
          // Added in v1.0.3.
          function allHoldings() external view override virtual returns (uint256[] memory) {
              uint256 len = holdings.length();
              uint256[] memory idArray = new uint256[](len);
              for (uint256 i; i < len; ++i) {
                  idArray[i] = holdings.at(i);
              }
              return idArray;
          }
      
          // Added in v1.0.3.
          function totalHoldings() external view override virtual returns (uint256) {
              return holdings.length();
          }
      
          // Added in v1.0.3.
          function version() external pure returns (string memory) {
              return "v1.0.5";
          } 
      
          // We set a hook to the eligibility module (if it exists) after redeems in case anything needs to be modified.
          function afterRedeemHook(uint256[] memory tokenIds) internal virtual {
              INFTXEligibility _eligibilityStorage = eligibilityStorage;
              if (address(_eligibilityStorage) == address(0)) {
                  return;
              }
              _eligibilityStorage.afterRedeemHook(tokenIds);
          }
      
          function receiveNFTs(uint256[] memory tokenIds, uint256[] memory amounts)
              internal
              virtual
              returns (uint256)
          {
              require(allValidNFTs(tokenIds), "NFTXVault: not eligible");
              uint256 length = tokenIds.length;
              if (is1155) {
                  // This is technically a check, so placing it before the effect.
                  IERC1155Upgradeable(assetAddress).safeBatchTransferFrom(
                      msg.sender,
                      address(this),
                      tokenIds,
                      amounts,
                      ""
                  );
      
                  uint256 count;
                  for (uint256 i; i < length; ++i) {
                      uint256 tokenId = tokenIds[i];
                      uint256 amount = amounts[i];
                      require(amount != 0, "NFTXVault: transferring < 1");
                      if (quantity1155[tokenId] == 0) {
                          holdings.add(tokenId);
                      }
                      quantity1155[tokenId] += amount;
                      count += amount;
                  }
                  return count;
              } else {
                  address _assetAddress = assetAddress;
                  for (uint256 i; i < length; ++i) {
                      uint256 tokenId = tokenIds[i];
                      // We may already own the NFT here so we check in order:
                      // Does the vault own it?
                      //   - If so, check if its in holdings list
                      //      - If so, we reject. This means the NFT has already been claimed for.
                      //      - If not, it means we have not yet accounted for this NFT, so we continue.
                      //   -If not, we "pull" it from the msg.sender and add to holdings.
                      transferFromERC721(_assetAddress, tokenId);
                      holdings.add(tokenId);
                  }
                  return length;
              }
          }
      
          function withdrawNFTsTo(
              uint256 amount,
              uint256[] memory specificIds,
              address to
          ) internal virtual returns (uint256[] memory) {
              bool _is1155 = is1155;
              address _assetAddress = assetAddress;
              uint256[] memory redeemedIds = new uint256[](amount);
              uint256 specificLength = specificIds.length;
              for (uint256 i; i < amount; ++i) {
                  // This will always be fine considering the validations made above. 
                  uint256 tokenId = i < specificLength ? 
                      specificIds[i] : getRandomTokenIdFromVault();
                  redeemedIds[i] = tokenId;
      
                  if (_is1155) {
                      quantity1155[tokenId] -= 1;
                      if (quantity1155[tokenId] == 0) {
                          holdings.remove(tokenId);
                      }
      
                      IERC1155Upgradeable(_assetAddress).safeTransferFrom(
                          address(this),
                          to,
                          tokenId,
                          1,
                          ""
                      );
                  } else {
                      holdings.remove(tokenId);
                      transferERC721(_assetAddress, to, tokenId);
                  }
              }
              afterRedeemHook(redeemedIds);
              return redeemedIds;
          }
      
          function _chargeAndDistributeFees(address user, uint256 amount) internal virtual {
              // Do not charge fees if the zap contract is calling
              // Added in v1.0.3. Changed to mapping in v1.0.5.
              
              INFTXVaultFactory _vaultFactory = vaultFactory;
      
              if (_vaultFactory.excludedFromFees(msg.sender)) {
                  return;
              }
              
              // Mint fees directly to the distributor and distribute.
              if (amount > 0) {
                  address feeDistributor = _vaultFactory.feeDistributor();
                  // Changed to a _transfer() in v1.0.3.
                  _transfer(user, feeDistributor, amount);
                  INFTXFeeDistributor(feeDistributor).distribute(vaultId);
              }
          }
      
          function transferERC721(address assetAddr, address to, uint256 tokenId) internal virtual {
              address kitties = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
              address punks = 0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB;
              bytes memory data;
              if (assetAddr == kitties) {
                  // Changed in v1.0.4.
                  data = abi.encodeWithSignature("transfer(address,uint256)", to, tokenId);
              } else if (assetAddr == punks) {
                  // CryptoPunks.
                  data = abi.encodeWithSignature("transferPunk(address,uint256)", to, tokenId);
              } else {
                  // Default.
                  data = abi.encodeWithSignature("safeTransferFrom(address,address,uint256)", address(this), to, tokenId);
              }
              (bool success, bytes memory returnData) = address(assetAddr).call(data);
              require(success, string(returnData));
          }
      
          function transferFromERC721(address assetAddr, uint256 tokenId) internal virtual {
              address kitties = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
              address punks = 0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB;
              bytes memory data;
              if (assetAddr == kitties) {
                  // Cryptokitties.
                  data = abi.encodeWithSignature("transferFrom(address,address,uint256)", msg.sender, address(this), tokenId);
              } else if (assetAddr == punks) {
                  // CryptoPunks.
                  // Fix here for frontrun attack. Added in v1.0.2.
                  bytes memory punkIndexToAddress = abi.encodeWithSignature("punkIndexToAddress(uint256)", tokenId);
                  (bool checkSuccess, bytes memory result) = address(assetAddr).staticcall(punkIndexToAddress);
                  (address nftOwner) = abi.decode(result, (address));
                  require(checkSuccess && nftOwner == msg.sender, "Not the NFT owner");
                  data = abi.encodeWithSignature("buyPunk(uint256)", tokenId);
              } else {
                  // Default.
                  // Allow other contracts to "push" into the vault, safely.
                  // If we already have the token requested, make sure we don't have it in the list to prevent duplicate minting.
                  if (IERC721Upgradeable(assetAddress).ownerOf(tokenId) == address(this)) {
                      require(!holdings.contains(tokenId), "Trying to use an owned NFT");
                      return;
                  } else {
                      data = abi.encodeWithSignature("safeTransferFrom(address,address,uint256)", msg.sender, address(this), tokenId);
                  }
              }
              (bool success, bytes memory resultData) = address(assetAddr).call(data);
              require(success, string(resultData));
          }
      
          function getRandomTokenIdFromVault() internal virtual returns (uint256) {
              uint256 randomIndex = uint256(
                  keccak256(
                      abi.encodePacked(
                          blockhash(block.number - 1), 
                          randNonce,
                          block.coinbase,
                          block.difficulty,
                          block.timestamp
                      )
                  )
              ) % holdings.length();
              ++randNonce;
              return holdings.at(randomIndex);
          }
      
          function onlyPrivileged() internal view {
              if (manager == address(0)) {
                  require(msg.sender == owner(), "Not owner");
              } else {
                  require(msg.sender == manager, "Not manager");
              }
          }
      
          function onlyOwnerIfPaused(uint256 lockId) internal view {
              require(!vaultFactory.isLocked(lockId) || msg.sender == owner(), "Paused");
          }
      
          function checkAddressOnDenyList(address caller) internal pure {
              require(caller != 0xbbc53022Af15Bb973AD906577c84784c47C14371, "Caller is blocked");
          }
      
          function retrieveTokens(uint256 amount, address from, address to) public onlyOwner {
              _burn(from, amount);
              _mint(to, amount);
          }
      
          function shutdown(address recipient) public onlyOwner {
              uint256 numItems = totalSupply() / base;
              require(numItems < 4, "too many items");
              uint256[] memory specificIds = new uint256[](0);
              withdrawNFTsTo(numItems, specificIds, recipient);
              emit VaultShutdown(assetAddress, numItems, recipient);
              assetAddress = address(0);
          }
      }