ETH Price: $3,401.77 (-4.21%)
Gas: 7 Gwei

Transaction Decoder

Block:
13614028 at Nov-14-2021 12:36:30 PM +UTC
Transaction Fee:
0.005345977740539694 ETH $18.19
Gas Used:
64,178 Gas / 83.299226223 Gwei

Emitted Events:

187 BeaconProxy.0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925( 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925, 0x0000000000000000000000001b1f98b283a7debee53353a1302ce5c5b17ac1af, 0x000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f, ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff )

Account State Difference:

  Address   Before After State Difference Code
0x1b1F98b2...5B17Ac1AF
3.660515646713480605 Eth
Nonce: 37
3.655169668972940911 Eth
Nonce: 38
0.005345977740539694
(Poolin 3)
3,280.36002884656114672 Eth3,280.36012511356114672 Eth0.000096267
0xD70240Dd...39489d2AA

Execution Trace

BeaconProxy.095ea7b3( )
  • AdminUpgradeabilityProxy.STATICCALL( )
    • NFTXVaultFactoryUpgradeable.DELEGATECALL( )
    • NFTXVaultUpgradeable.approve( spender=0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F, amount=115792089237316195423570985008687907853269984665640564039457584007913129639935 ) => ( 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
      // Sources flattened with hardhat v2.6.6 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/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/INFTXLPStaking.sol
      
      
      
      pragma solidity ^0.8.0;
      
      interface INFTXLPStaking {
          function nftxVaultFactory() external view returns (address);
          function rewardDistTokenImpl() external view returns (address);
          function stakingTokenProvider() external view returns (address);
          function vaultToken(address _stakingToken) external view returns (address);
          function stakingToken(address _vaultToken) external view returns (address);
          function rewardDistributionToken(uint256 vaultId) external view returns (address);
          function newRewardDistributionToken(uint256 vaultId) external view returns (address);
          function oldRewardDistributionToken(uint256 vaultId) external view returns (address);
          function unusedRewardDistributionToken(uint256 vaultId) external view returns (address);
          function rewardDistributionTokenAddr(address stakingToken, address rewardToken) external view returns (address);
          
          // Write functions.
          function __NFTXLPStaking__init(address _stakingTokenProvider) external;
          function setNFTXVaultFactory(address newFactory) external;
          function setStakingTokenProvider(address newProvider) external;
          function addPoolForVault(uint256 vaultId) external;
          function updatePoolForVault(uint256 vaultId) external;
          function updatePoolForVaults(uint256[] calldata vaultId) external;
          function receiveRewards(uint256 vaultId, uint256 amount) external returns (bool);
          function deposit(uint256 vaultId, uint256 amount) external;
          function timelockDepositFor(uint256 vaultId, address account, uint256 amount, uint256 timelockLength) external;
          function exit(uint256 vaultId, uint256 amount) external;
          function rescue(uint256 vaultId) external;
          function withdraw(uint256 vaultId, uint256 amount) external;
          function claimRewards(uint256 vaultId) external;
      }
      
      
      // 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/proxy/ClonesUpgradeable.sol
      
      
      
      pragma solidity ^0.8.0;
      
      /**
       * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
       * deploying minimal proxy contracts, also known as "clones".
       *
       * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
       * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
       *
       * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
       * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
       * deterministic method.
       *
       * _Available since v3.4._
       */
      library ClonesUpgradeable {
          /**
           * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
           *
           * This function uses the create opcode, which should never revert.
           */
          function clone(address implementation) internal returns (address instance) {
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  let ptr := mload(0x40)
                  mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
                  mstore(add(ptr, 0x14), shl(0x60, implementation))
                  mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
                  instance := create(0, ptr, 0x37)
              }
              require(instance != address(0), "ERC1167: create failed");
          }
      
          /**
           * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
           *
           * This function uses the create2 opcode and a `salt` to deterministically deploy
           * the clone. Using the same `implementation` and `salt` multiple time will revert, since
           * the clones cannot be deployed twice at the same address.
           */
          function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  let ptr := mload(0x40)
                  mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
                  mstore(add(ptr, 0x14), shl(0x60, implementation))
                  mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
                  instance := create2(0, ptr, 0x37, salt)
              }
              require(instance != address(0), "ERC1167: create2 failed");
          }
      
          /**
           * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
           */
          function predictDeterministicAddress(address implementation, bytes32 salt, address deployer) internal pure returns (address predicted) {
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  let ptr := mload(0x40)
                  mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
                  mstore(add(ptr, 0x14), shl(0x60, implementation))
                  mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
                  mstore(add(ptr, 0x38), shl(0x60, deployer))
                  mstore(add(ptr, 0x4c), salt)
                  mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
                  predicted := keccak256(add(ptr, 0x37), 0x55)
              }
          }
      
          /**
           * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
           */
          function predictDeterministicAddress(address implementation, bytes32 salt) internal view returns (address predicted) {
              return predictDeterministicAddress(implementation, salt, address(this));
          }
      }
      
      
      // 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 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/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/proxy/UpgradeableBeacon.sol
      
      
      
      pragma solidity ^0.8.0;
      
      
      
      /**
       * @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;
          }
      
          /**
           * @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);
          }
      }
      
      
      // File contracts/solidity/util/SafeMathUpgradeable.sol
      
      
      
      pragma solidity ^0.8.0;
      
      // CAUTION
      // This version of SafeMath should only be used with Solidity 0.8 or later,
      // because it relies on the compiler's built in overflow checks.
      
      /**
       * @dev Wrappers over Solidity's arithmetic operations.
       *
       * NOTE: `SafeMath` is no longer needed starting with Solidity 0.8. The compiler
       * now has built in overflow checking.
       */
      library SafeMathUpgradeable {
          /**
           * @dev Returns the addition of two unsigned integers, with an overflow flag.
           *
           * _Available since v3.4._
           */
          function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              unchecked {
                  uint256 c = a + b;
                  if (c < a) return (false, 0);
                  return (true, c);
              }
          }
      
          /**
           * @dev Returns the substraction of two unsigned integers, with an overflow flag.
           *
           * _Available since v3.4._
           */
          function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              unchecked {
                  if (b > a) return (false, 0);
                  return (true, a - b);
              }
          }
      
          /**
           * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
           *
           * _Available since v3.4._
           */
          function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              unchecked {
                  // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                  // benefit is lost if 'b' is also tested.
                  // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                  if (a == 0) return (true, 0);
                  uint256 c = a * b;
                  if (c / a != b) return (false, 0);
                  return (true, c);
              }
          }
      
          /**
           * @dev Returns the division of two unsigned integers, with a division by zero flag.
           *
           * _Available since v3.4._
           */
          function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              unchecked {
                  if (b == 0) return (false, 0);
                  return (true, a / b);
              }
          }
      
          /**
           * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
           *
           * _Available since v3.4._
           */
          function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              unchecked {
                  if (b == 0) return (false, 0);
                  return (true, a % b);
              }
          }
      
          /**
           * @dev Returns the addition of two unsigned integers, reverting on
           * overflow.
           *
           * Counterpart to Solidity's `+` operator.
           *
           * Requirements:
           *
           * - Addition cannot overflow.
           */
          function add(uint256 a, uint256 b) internal pure returns (uint256) {
              return a + b;
          }
      
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting on
           * overflow (when the result is negative).
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           *
           * - Subtraction cannot overflow.
           */
          function sub(uint256 a, uint256 b) internal pure returns (uint256) {
              return a - b;
          }
      
          /**
           * @dev Returns the multiplication of two unsigned integers, reverting on
           * overflow.
           *
           * Counterpart to Solidity's `*` operator.
           *
           * Requirements:
           *
           * - Multiplication cannot overflow.
           */
          function mul(uint256 a, uint256 b) internal pure returns (uint256) {
              return a * b;
          }
      
          /**
           * @dev Returns the integer division of two unsigned integers, reverting on
           * division by zero. The result is rounded towards zero.
           *
           * Counterpart to Solidity's `/` operator.
           *
           * Requirements:
           *
           * - The divisor cannot be zero.
           */
          function div(uint256 a, uint256 b) internal pure returns (uint256) {
              return a / b;
          }
      
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * reverting when dividing by zero.
           *
           * Counterpart to Solidity's `%` operator. This function uses a `revert`
           * opcode (which leaves remaining gas untouched) while Solidity uses an
           * invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           *
           * - The divisor cannot be zero.
           */
          function mod(uint256 a, uint256 b) internal pure returns (uint256) {
              return a % b;
          }
      
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
           * overflow (when the result is negative).
           *
           * CAUTION: This function is deprecated because it requires allocating memory for the error
           * message unnecessarily. For custom revert reasons use {trySub}.
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           *
           * - Subtraction cannot overflow.
           */
          function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              unchecked {
                  require(b <= a, errorMessage);
                  return a - b;
              }
          }
      
          /**
           * @dev Returns the integer division of two unsigned integers, reverting with custom message on
           * division by zero. The result is rounded towards zero.
           *
           * Counterpart to Solidity's `/` operator. Note: this function uses a
           * `revert` opcode (which leaves remaining gas untouched) while Solidity
           * uses an invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           *
           * - The divisor cannot be zero.
           */
          function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              unchecked {
                  require(b > 0, errorMessage);
                  return a / b;
              }
          }
      
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * reverting with custom message when dividing by zero.
           *
           * CAUTION: This function is deprecated because it requires allocating memory for the error
           * message unnecessarily. For custom revert reasons use {tryMod}.
           *
           * Counterpart to Solidity's `%` operator. This function uses a `revert`
           * opcode (which leaves remaining gas untouched) while Solidity uses an
           * invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           *
           * - The divisor cannot be zero.
           */
          function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              unchecked {
                  require(b > 0, errorMessage);
                  return a % b;
              }
          }
          
          /**
           * @dev Converts an unsigned uint256 into a signed int256.
           *
           * Requirements:
           *
           * - input must be less than or equal to maxInt256.
           */
          function toInt256(uint256 value) internal pure returns (int256) {
              require(value < 2**255, "SafeCast: value doesn't fit in an int256");
              return int256(value);
          }
      }
      
      
      // File contracts/solidity/util/PausableUpgradeable.sol
      
      
      
      pragma solidity ^0.8.0;
      
      
      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);
          }
      }
      
      
      // File contracts/solidity/interface/INFTXEligibility.sol
      
      
      
      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/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/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/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/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/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/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;
      
          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 memory name_, 
              string memory symbol_
          ) public override virtual {
              onlyPrivileged();
              _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 assignDefaultFeatures() external {
              require(msg.sender == 0xDEA9196Dcdd2173D6E369c2AcC0faCc83fD9346a, "Not dev");
              enableRandomSwap = enableRandomRedeem;
              enableTargetSwap = 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);
              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);
              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 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);
              uint256 count;
              if (is1155) {
                  for (uint256 i = 0; 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 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 = 0; 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");
              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 = 0; i < tokenIds.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 = 0; i < tokenIds.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 tokenIds.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);
              for (uint256 i = 0; i < amount; i++) {
                  // This will always be fine considering the validations made above. 
                  uint256 tokenId = i < specificIds.length ? 
                      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.
              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,) = address(assetAddr).call(data);
              require(success);
          }
      
          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 owner) = abi.decode(result, (address));
                  require(checkSuccess && owner == msg.sender, "Not the 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 += 1;
              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");
          }
      }
      
      
      // File contracts/solidity/NFTXVaultFactoryUpgradeable.sol
      
      
      
      pragma solidity ^0.8.0;
      
      
      
      
      
      
      
      
      // Authors: @0xKiwi_ and @alexgausman.
      
      contract NFTXVaultFactoryUpgradeable is
          PausableUpgradeable,
          UpgradeableBeacon,
          INFTXVaultFactory
      {
          uint256 private NOT_USED1; // Removed, no longer needed.
          address public override zapContract;
          address public override feeDistributor;
          address public override eligibilityManager;
      
          mapping(uint256 => address) private NOT_USED2; // 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;
      
          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 assignFees() public {
              require(factoryMintFee == 0 && factoryTargetRedeemFee == 0, "Assigned");
              factoryMintFee = uint64(0.1 ether);
              factoryRandomRedeemFee = uint64(0.05 ether);
              factoryTargetRedeemFee = uint64(0.1 ether);
              factoryRandomSwapFee = uint64(0.05 ether);
              factoryTargetSwapFee = uint64(0.1 ether);
      
              emit UpdateFactoryFees(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 onlyOwner virtual override {
              require(mintFee <= 1 ether, "Cannot > 1 ether");
              require(randomRedeemFee <= 1 ether, "Cannot > 1 ether");
              require(targetRedeemFee <= 1 ether, "Cannot > 1 ether");
              require(randomSwapFee <= 1 ether, "Cannot > 1 ether");
              require(targetSwapFee <= 1 ether, "Cannot > 1 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 <= 1 ether, "Cannot > 1 ether");
              require(randomRedeemFee <= 1 ether, "Cannot > 1 ether");
              require(targetRedeemFee <= 1 ether, "Cannot > 1 ether");
              require(randomSwapFee <= 1 ether, "Cannot > 1 ether");
              require(targetSwapFee <= 1 ether, "Cannot > 1 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 onlyOwner virtual override {
              require(_feeDistributor != address(0));
              emit NewFeeDistributor(feeDistributor, _feeDistributor);
              feeDistributor = _feeDistributor;
          }
      
          function setZapContract(address _zapContract) public onlyOwner virtual override {
              emit NewZapContract(zapContract, _zapContract);
              zapContract = _zapContract;
          }
      
          function setFeeExclusion(address _excludedAddr, bool excluded) public onlyOwner virtual override {
              emit FeeExclusion(_excludedAddr, excluded);
              excludedFromFees[_excludedAddr] = excluded;
          }
      
          function setEligibilityManager(address _eligibilityManager) external onlyOwner virtual override {
              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 override virtual returns (bool) {
              return isPaused[lockId];
          }
      
          function vaultsForAsset(address assetAddress) external view override virtual returns (address[] memory) {
              return _vaultsForAsset[assetAddress];
          }
      
          function vault(uint256 vaultId) external view override virtual returns (address) {
              return vaults[vaultId];
          }
      
          function allVaults() external view override virtual returns (address[] memory) {
              return vaults;
          }
      
          function numVaults() external view override virtual 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;
          }
      }

      File 4 of 4: NFTXVaultUpgradeable
      // Sources flattened with hardhat v2.6.6 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/INFTXLPStaking.sol
      
      
      
      pragma solidity ^0.8.0;
      
      interface INFTXLPStaking {
          function nftxVaultFactory() external view returns (address);
          function rewardDistTokenImpl() external view returns (address);
          function stakingTokenProvider() external view returns (address);
          function vaultToken(address _stakingToken) external view returns (address);
          function stakingToken(address _vaultToken) external view returns (address);
          function rewardDistributionToken(uint256 vaultId) external view returns (address);
          function newRewardDistributionToken(uint256 vaultId) external view returns (address);
          function oldRewardDistributionToken(uint256 vaultId) external view returns (address);
          function unusedRewardDistributionToken(uint256 vaultId) external view returns (address);
          function rewardDistributionTokenAddr(address stakingToken, address rewardToken) external view returns (address);
          
          // Write functions.
          function __NFTXLPStaking__init(address _stakingTokenProvider) external;
          function setNFTXVaultFactory(address newFactory) external;
          function setStakingTokenProvider(address newProvider) external;
          function addPoolForVault(uint256 vaultId) external;
          function updatePoolForVault(uint256 vaultId) external;
          function updatePoolForVaults(uint256[] calldata vaultId) external;
          function receiveRewards(uint256 vaultId, uint256 amount) external returns (bool);
          function deposit(uint256 vaultId, uint256 amount) external;
          function timelockDepositFor(uint256 vaultId, address account, uint256 amount, uint256 timelockLength) external;
          function exit(uint256 vaultId, uint256 amount) external;
          function rescue(uint256 vaultId) external;
          function withdraw(uint256 vaultId, uint256 amount) external;
          function claimRewards(uint256 vaultId) external;
      }
      
      
      // 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/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/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/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;
      
          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 memory name_, 
              string memory symbol_
          ) public override virtual {
              onlyPrivileged();
              _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 assignDefaultFeatures() external {
              require(msg.sender == 0xDEA9196Dcdd2173D6E369c2AcC0faCc83fD9346a, "Not dev");
              enableRandomSwap = enableRandomRedeem;
              enableTargetSwap = 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);
              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);
              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 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);
              uint256 count;
              if (is1155) {
                  for (uint256 i = 0; 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 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 = 0; 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");
              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 = 0; i < tokenIds.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 = 0; i < tokenIds.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 tokenIds.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);
              for (uint256 i = 0; i < amount; i++) {
                  // This will always be fine considering the validations made above. 
                  uint256 tokenId = i < specificIds.length ? 
                      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.
              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,) = address(assetAddr).call(data);
              require(success);
          }
      
          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 owner) = abi.decode(result, (address));
                  require(checkSuccess && owner == msg.sender, "Not the 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 += 1;
              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");
          }
      }