ETH Price: $2,110.26 (+1.65%)

Transaction Decoder

Block:
18859904 at Dec-25-2023 02:53:11 AM +UTC
Transaction Fee:
0.002362744118035632 ETH $4.99
Gas Used:
162,896 Gas / 14.504617167 Gwei

Emitted Events:

223 KompeteGameAsset.TransferSingle( operator=KompeteGameAssetFactory, from=0x00000000...000000000, to=[Sender] 0xee008db59ec462879cd355d348e7dde74f65ecbb, id=41, value=1 )
224 KompeteMarketplace.OrdersMatched( buyHash=0000000000000000000000000000000000000000000000000000000000000000, sellHash=ACAC360EB6E72B11B458CAADA2450DFBA91B46EFE2BA66F65367199D4412BDBF, maker=0x3ec2fee6...4556Fbe29, taker=[Sender] 0xee008db59ec462879cd355d348e7dde74f65ecbb, price=40000000000000000, metadata=0000000000000000000000000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
(beaverbuild)
19.969960547651144206 Eth19.969976837251144206 Eth0.0000162896
0x9feca571...6E13dC6fa
0xc149B933...80bF3858d
0xEe008dB5...74F65ecBB
0.386501226443055881 Eth
Nonce: 256
0.344138482325020249 Eth
Nonce: 257
0.042362744118035632
0xFEF90843...a8D6252F8 104.872606806731432489 Eth104.912606806731432489 Eth0.04

Execution Trace

ETH 0.04 KompeteMarketplace.atomicMatch_( buy=[{name:maker, type:address, order:1, indexed:false, value:0xEe008dB59ec462879cD355d348e7dDe74F65ecBB, valueString:0xEe008dB59ec462879cD355d348e7dDe74F65ecBB}, {name:taker, type:address, order:2, indexed:false, value:0x0000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000}, {name:makerProtocolFee, type:uint256, order:3, indexed:false, value:9000, valueString:9000}, {name:takerProtocolFee, type:uint256, order:4, indexed:false, value:0, valueString:0}, {name:feeRecipient, type:address, order:5, indexed:false, value:0x0000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000}, {name:side, type:uint8, order:6, indexed:false, value:0, valueString:0}, {name:collectionType, type:uint8, order:7, indexed:false, value:1, valueString:1}, {name:mintFactory, type:address, order:8, indexed:false, value:0xBdd0D42F51f5865391CfD988DF1d536f97dC7dc6, valueString:0xBdd0D42F51f5865391CfD988DF1d536f97dC7dc6}, {name:collection, type:address, order:9, indexed:false, value:0xc149B933DF9C68b1e4cb0656958C66e80bF3858d, valueString:0xc149B933DF9C68b1e4cb0656958C66e80bF3858d}, {name:tokenIds, type:uint256[], order:10, indexed:false, value:[41], valueString:[41]}, {name:amounts, type:uint256[], order:11, indexed:false, value:[1], valueString:[1]}, {name:paymentToken, type:address, order:12, indexed:false, value:0x0000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000}, {name:basePrice, type:uint256, order:13, indexed:false, value:40000000000000000, valueString:40000000000000000}, {name:extra, type:uint256, order:14, indexed:false, value:0, valueString:0}, {name:listingTime, type:uint256, order:15, indexed:false, value:1703472773, valueString:1703472773}, {name:expirationTime, type:uint256, order:16, indexed:false, value:0, valueString:0}, {name:salt, type:uint256, order:17, indexed:false, value:98497486276936879200169045308303553195321099979172372591770789284318287500516, valueString:98497486276936879200169045308303553195321099979172372591770789284318287500516}], buySig=0x0E25C10FCFE5F7F6AC8BE59A3D710C832E78C070817AA7446CCED699413C7D7C5DF82DE2D168C3AD7B095DAD2498C4DC26C3893A2F5FCF903DF617EDA75DE1331B, sell=[{name:maker, type:address, order:1, indexed:false, value:0x3ec2fee6E206eF889FDE300e799A2EC4556Fbe29, valueString:0x3ec2fee6E206eF889FDE300e799A2EC4556Fbe29}, {name:taker, type:address, order:2, indexed:false, value:0x0000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000}, {name:makerProtocolFee, type:uint256, order:3, indexed:false, value:9000, valueString:9000}, {name:takerProtocolFee, type:uint256, order:4, indexed:false, value:0, valueString:0}, {name:feeRecipient, type:address, order:5, indexed:false, value:0xFEF90843630d43869877769DE31fC9Aa8D6252F8, valueString:0xFEF90843630d43869877769DE31fC9Aa8D6252F8}, {name:side, type:uint8, order:6, indexed:false, value:1, valueString:1}, {name:collectionType, type:uint8, order:7, indexed:false, value:1, valueString:1}, {name:mintFactory, type:address, order:8, indexed:false, value:0xBdd0D42F51f5865391CfD988DF1d536f97dC7dc6, valueString:0xBdd0D42F51f5865391CfD988DF1d536f97dC7dc6}, {name:collection, type:address, order:9, indexed:false, value:0xc149B933DF9C68b1e4cb0656958C66e80bF3858d, valueString:0xc149B933DF9C68b1e4cb0656958C66e80bF3858d}, {name:tokenIds, type:uint256[], order:10, indexed:false, value:[41], valueString:[41]}, {name:amounts, type:uint256[], order:11, indexed:false, value:[1], valueString:[1]}, {name:paymentToken, type:address, order:12, indexed:false, value:0x0000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000}, {name:basePrice, type:uint256, order:13, indexed:false, value:40000000000000000, valueString:40000000000000000}, {name:extra, type:uint256, order:14, indexed:false, value:0, valueString:0}, {name:listingTime, type:uint256, order:15, indexed:false, value:1703472772, valueString:1703472772}, {name:expirationTime, type:uint256, order:16, indexed:false, value:0, valueString:0}, {name:salt, type:uint256, order:17, indexed:false, value:86519409025794614891503577949601772782054094259681758478179997655962972704560, valueString:86519409025794614891503577949601772782054094259681758478179997655962972704560}], sellSig=0x0564EA88C91B6D1277F617472A2B6EC525C8487E4B73521B4C2037F6C0A8F97F04EDD7E361B15F01FF0F4510314F7FA6CA923A6C842CE7C914A0B7D7C793CAC91C, metadata=0000000000000000000000000000000000000000000000000000000000000000 )
  • Null: 0x000...001.acac360e( )
  • KompeteGameAssetFactory.canMint( collection=0xc149B933DF9C68b1e4cb0656958C66e80bF3858d, account=0x3ec2fee6E206eF889FDE300e799A2EC4556Fbe29 ) => ( True )
  • ETH 0.036 0xfef90843630d43869877769de31fc9aa8d6252f8.CALL( )
  • ETH 0.004 0xfef90843630d43869877769de31fc9aa8d6252f8.CALL( )
  • KompeteGameAssetFactory.mint( to=0xEe008dB59ec462879cD355d348e7dDe74F65ecBB, id=41, amount=1, data=0x )
    • KompeteGameAsset.mint( to=0xEe008dB59ec462879cD355d348e7dDe74F65ecBB, id=41, amount=1, data=0x )
      File 1 of 3: KompeteMarketplace
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "@openzeppelin/contracts/access/Ownable.sol";
      import "@openzeppelin/contracts/security/Pausable.sol";
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
      import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
      import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
      import "./exchange/ExchangeCore.sol";
      contract KompeteMarketplace is ExchangeCore, Ownable, Pausable {
          string public constant NAME = "Kompete Marketplace";
          string public constant VERSION = "1.0";
          string public constant CODENAME = "Late rabbit";
          event ProtocolFeeRecipientChanged(address indexed recipient);
          event MintFeeRecipientChanged(address indexed collection, address indexed recipient);
          event CollectionAdded(address indexed collection);
          event CollectionRemoved(address indexed collection);
          constructor(IERC20 tokenAddress, address protocolFeeAddress) EIP712(NAME, VERSION) {
              exchangeToken = tokenAddress;
              protocolFeeRecipient = protocolFeeAddress;
          }
          /**
           * @dev Change the protocol fee recipient (admins only)
           * @param recipient New protocol fee recipient address
           */
          function setProtocolFeeRecipient(address recipient) external onlyOwner {
              protocolFeeRecipient = recipient;
              emit ProtocolFeeRecipientChanged(recipient);
          }
          /**
           * @dev Change the mint fee recipient for a collection (admins only)
           * @param recipient New protocol fee recipient address (set collection address(0) for default value)
           */
          function setMintFeeRecipient(address collection, address recipient) external onlyOwner {
              mintFeeRecipient[collection] = recipient;
              emit MintFeeRecipientChanged(collection, recipient);
          }
          /**
           * @dev Allow/Disallow a collection to be traded in the marketplace (admins only)
           */
          function toggleCollection(address collection, bool allowed) external onlyOwner {
              if (collection == address(0)) revert InvalidCollection();
              if (allowedCollections[collection] != allowed) {
                  allowedCollections[collection] = allowed;
                  if (allowed) emit CollectionAdded(collection);
                  else emit CollectionRemoved(collection);
              }
          }
          /**
           * @dev Call hashOrder
           */
          function hashOrder_(Order memory order) public view returns (bytes32) {
              return hashOrder(order, nonces[order.maker]);
          }
          /**
           * @dev Call hashToSign
           */
          function hashToSign_(Order memory order) public view returns (bytes32) {
              return hashToSign(order, nonces[order.maker]);
          }
          /**
           * @dev Call validateOrderParameters
           */
          function validateOrderParameters_(Order memory order) public view returns (bool) {
              return validateOrderParameters(order);
          }
          /**
           * @dev Call validateOrder
           */
          function validateOrder_(Order memory order, bytes memory signature) public view returns (bool) {
              return validateOrder(hashToSign(order, nonces[order.maker]), order, signature);
          }
          /**
           * @dev Call approveOrder
           */
          function approveOrder_(Order memory order, bool orderbookInclusionDesired) external whenNotPaused {
              return approveOrder(order, orderbookInclusionDesired);
          }
          /**
           * @dev Call cancelOrder
           */
          function cancelOrder_(Order memory order, bytes memory signature) external whenNotPaused {
              return cancelOrder(order, signature, nonces[order.maker]);
          }
          /**
           * @dev Call cancelOrder, supplying a specific nonce — enables cancelling orders
           that were signed with nonces greater than the current nonce.
           */
          function cancelOrderWithNonce_(
              Order memory order,
              bytes memory signature,
              uint256 nonce
          ) external {
              return cancelOrder(order, signature, nonce);
          }
          /**
           * @dev Call ordersCanMatch
           */
          function ordersCanMatch_(Order memory buy, Order memory sell) public view returns (bool) {
              return ordersCanMatch(buy, sell);
          }
          /**
           * @dev Atomically match two orders
           * @param buy Buy-side order
           * @param buySig Buy-side order signature
           * @param sell Sell-side order
           * @param sellSig Sell-side order signature
           */
          function atomicMatch_(
              Order memory buy,
              bytes memory buySig,
              Order memory sell,
              bytes memory sellSig,
              bytes32 metadata
          ) external payable whenNotPaused {
              atomicMatch(buy, buySig, sell, sellSig, metadata);
          }
          /**
           * @dev Pauses the marketplace
           */
          function pause() external onlyOwner {
              _pause();
          }
          /**
           * @dev Unpauses the marketplace
           */
          function unpause() external onlyOwner {
              _unpause();
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
      pragma solidity ^0.8.0;
      import "../utils/Context.sol";
      /**
       * @dev Contract module which provides a basic access control mechanism, where
       * there is an account (an owner) that can be granted exclusive access to
       * specific functions.
       *
       * By default, the owner account will be the one that deploys the contract. This
       * can later be changed with {transferOwnership}.
       *
       * This module is used through inheritance. It will make available the modifier
       * `onlyOwner`, which can be applied to your functions to restrict their use to
       * the owner.
       */
      abstract contract Ownable is Context {
          address private _owner;
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          constructor() {
              _transferOwnership(_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 {
              _transferOwnership(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");
              _transferOwnership(newOwner);
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Internal function without access restriction.
           */
          function _transferOwnership(address newOwner) internal virtual {
              address oldOwner = _owner;
              _owner = newOwner;
              emit OwnershipTransferred(oldOwner, newOwner);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)
      pragma solidity ^0.8.0;
      import "../utils/Context.sol";
      /**
       * @dev Contract module which allows children to implement an emergency stop
       * mechanism that can be triggered by an authorized account.
       *
       * This module is used through inheritance. It will make available the
       * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
       * the functions of your contract. Note that they will not be pausable by
       * simply including this module, only once the modifiers are put in place.
       */
      abstract contract Pausable is Context {
          /**
           * @dev Emitted when the pause is triggered by `account`.
           */
          event Paused(address account);
          /**
           * @dev Emitted when the pause is lifted by `account`.
           */
          event Unpaused(address account);
          bool private _paused;
          /**
           * @dev Initializes the contract in unpaused state.
           */
          constructor() {
              _paused = false;
          }
          /**
           * @dev Returns true if the contract is paused, and false otherwise.
           */
          function paused() public view virtual returns (bool) {
              return _paused;
          }
          /**
           * @dev Modifier to make a function callable only when the contract is not paused.
           *
           * Requirements:
           *
           * - The contract must not be paused.
           */
          modifier whenNotPaused() {
              require(!paused(), "Pausable: paused");
              _;
          }
          /**
           * @dev Modifier to make a function callable only when the contract is paused.
           *
           * Requirements:
           *
           * - The contract must be paused.
           */
          modifier whenPaused() {
              require(paused(), "Pausable: not paused");
              _;
          }
          /**
           * @dev Triggers stopped state.
           *
           * Requirements:
           *
           * - The contract must not be paused.
           */
          function _pause() internal virtual whenNotPaused {
              _paused = true;
              emit Paused(_msgSender());
          }
          /**
           * @dev Returns to normal state.
           *
           * Requirements:
           *
           * - The contract must be paused.
           */
          function _unpause() internal virtual whenPaused {
              _paused = false;
              emit Unpaused(_msgSender());
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP.
       */
      interface IERC20 {
          /**
           * @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);
          /**
           * @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 `to`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address to, 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 `from` to `to` 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 from,
              address to,
              uint256 amount
          ) external returns (bool);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)
      pragma solidity ^0.8.0;
      import "../IERC20.sol";
      import "../../../utils/Address.sol";
      /**
       * @title SafeERC20
       * @dev Wrappers around ERC20 operations that throw on failure (when the token
       * contract returns false). Tokens that return no value (and instead revert or
       * throw on failure) are also supported, non-reverting calls are assumed to be
       * successful.
       * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
       * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
       */
      library SafeERC20 {
          using Address for address;
          function safeTransfer(
              IERC20 token,
              address to,
              uint256 value
          ) internal {
              _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
          }
          function safeTransferFrom(
              IERC20 token,
              address from,
              address to,
              uint256 value
          ) internal {
              _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
          }
          /**
           * @dev Deprecated. This function has issues similar to the ones found in
           * {IERC20-approve}, and its usage is discouraged.
           *
           * Whenever possible, use {safeIncreaseAllowance} and
           * {safeDecreaseAllowance} instead.
           */
          function safeApprove(
              IERC20 token,
              address spender,
              uint256 value
          ) internal {
              // safeApprove should only be called when setting an initial allowance,
              // or when resetting it to zero. To increase and decrease it, use
              // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
              require(
                  (value == 0) || (token.allowance(address(this), spender) == 0),
                  "SafeERC20: approve from non-zero to non-zero allowance"
              );
              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
          }
          function safeIncreaseAllowance(
              IERC20 token,
              address spender,
              uint256 value
          ) internal {
              uint256 newAllowance = token.allowance(address(this), spender) + value;
              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
          }
          function safeDecreaseAllowance(
              IERC20 token,
              address spender,
              uint256 value
          ) internal {
              unchecked {
                  uint256 oldAllowance = token.allowance(address(this), spender);
                  require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                  uint256 newAllowance = oldAllowance - value;
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
              }
          }
          /**
           * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
           * on the return value: the return value is optional (but if data is returned, it must not be false).
           * @param token The token targeted by the call.
           * @param data The call data (encoded using abi.encode or one of its variants).
           */
          function _callOptionalReturn(IERC20 token, bytes memory data) private {
              // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
              // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
              // the target address contains contract code and also asserts for success in the low-level call.
              bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
              if (returndata.length > 0) {
                  // Return data is optional
                  require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/cryptography/draft-EIP712.sol)
      pragma solidity ^0.8.0;
      import "./ECDSA.sol";
      /**
       * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
       *
       * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
       * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
       * they need in their contracts using a combination of `abi.encode` and `keccak256`.
       *
       * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
       * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
       * ({_hashTypedDataV4}).
       *
       * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
       * the chain id to protect against replay attacks on an eventual fork of the chain.
       *
       * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
       * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
       *
       * _Available since v3.4._
       */
      abstract contract EIP712 {
          /* solhint-disable var-name-mixedcase */
          // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
          // invalidate the cached domain separator if the chain id changes.
          bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
          uint256 private immutable _CACHED_CHAIN_ID;
          address private immutable _CACHED_THIS;
          bytes32 private immutable _HASHED_NAME;
          bytes32 private immutable _HASHED_VERSION;
          bytes32 private immutable _TYPE_HASH;
          /* solhint-enable var-name-mixedcase */
          /**
           * @dev Initializes the domain separator and parameter caches.
           *
           * The meaning of `name` and `version` is specified in
           * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
           *
           * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
           * - `version`: the current major version of the signing domain.
           *
           * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
           * contract upgrade].
           */
          constructor(string memory name, string memory version) {
              bytes32 hashedName = keccak256(bytes(name));
              bytes32 hashedVersion = keccak256(bytes(version));
              bytes32 typeHash = keccak256(
                  "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
              );
              _HASHED_NAME = hashedName;
              _HASHED_VERSION = hashedVersion;
              _CACHED_CHAIN_ID = block.chainid;
              _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
              _CACHED_THIS = address(this);
              _TYPE_HASH = typeHash;
          }
          /**
           * @dev Returns the domain separator for the current chain.
           */
          function _domainSeparatorV4() internal view returns (bytes32) {
              if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
                  return _CACHED_DOMAIN_SEPARATOR;
              } else {
                  return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
              }
          }
          function _buildDomainSeparator(
              bytes32 typeHash,
              bytes32 nameHash,
              bytes32 versionHash
          ) private view returns (bytes32) {
              return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
          }
          /**
           * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
           * function returns the hash of the fully encoded EIP712 message for this domain.
           *
           * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
           *
           * ```solidity
           * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
           *     keccak256("Mail(address to,string contents)"),
           *     mailTo,
           *     keccak256(bytes(mailContents))
           * )));
           * address signer = ECDSA.recover(digest, signature);
           * ```
           */
          function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
              return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol)
      pragma solidity ^0.8.0;
      import "../Strings.sol";
      /**
       * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
       *
       * These functions can be used to verify that a message was signed by the holder
       * of the private keys of a given address.
       */
      library ECDSA {
          enum RecoverError {
              NoError,
              InvalidSignature,
              InvalidSignatureLength,
              InvalidSignatureS,
              InvalidSignatureV
          }
          function _throwError(RecoverError error) private pure {
              if (error == RecoverError.NoError) {
                  return; // no error: do nothing
              } else if (error == RecoverError.InvalidSignature) {
                  revert("ECDSA: invalid signature");
              } else if (error == RecoverError.InvalidSignatureLength) {
                  revert("ECDSA: invalid signature length");
              } else if (error == RecoverError.InvalidSignatureS) {
                  revert("ECDSA: invalid signature 's' value");
              } else if (error == RecoverError.InvalidSignatureV) {
                  revert("ECDSA: invalid signature 'v' value");
              }
          }
          /**
           * @dev Returns the address that signed a hashed message (`hash`) with
           * `signature` or error string. This address can then be used for verification purposes.
           *
           * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
           * this function rejects them by requiring the `s` value to be in the lower
           * half order, and the `v` value to be either 27 or 28.
           *
           * IMPORTANT: `hash` _must_ be the result of a hash operation for the
           * verification to be secure: it is possible to craft signatures that
           * recover to arbitrary addresses for non-hashed data. A safe way to ensure
           * this is by receiving a hash of the original message (which may otherwise
           * be too long), and then calling {toEthSignedMessageHash} on it.
           *
           * Documentation for signature generation:
           * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
           * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
           *
           * _Available since v4.3._
           */
          function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
              // Check the signature length
              // - case 65: r,s,v signature (standard)
              // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
              if (signature.length == 65) {
                  bytes32 r;
                  bytes32 s;
                  uint8 v;
                  // ecrecover takes the signature parameters, and the only way to get them
                  // currently is to use assembly.
                  assembly {
                      r := mload(add(signature, 0x20))
                      s := mload(add(signature, 0x40))
                      v := byte(0, mload(add(signature, 0x60)))
                  }
                  return tryRecover(hash, v, r, s);
              } else if (signature.length == 64) {
                  bytes32 r;
                  bytes32 vs;
                  // ecrecover takes the signature parameters, and the only way to get them
                  // currently is to use assembly.
                  assembly {
                      r := mload(add(signature, 0x20))
                      vs := mload(add(signature, 0x40))
                  }
                  return tryRecover(hash, r, vs);
              } else {
                  return (address(0), RecoverError.InvalidSignatureLength);
              }
          }
          /**
           * @dev Returns the address that signed a hashed message (`hash`) with
           * `signature`. This address can then be used for verification purposes.
           *
           * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
           * this function rejects them by requiring the `s` value to be in the lower
           * half order, and the `v` value to be either 27 or 28.
           *
           * IMPORTANT: `hash` _must_ be the result of a hash operation for the
           * verification to be secure: it is possible to craft signatures that
           * recover to arbitrary addresses for non-hashed data. A safe way to ensure
           * this is by receiving a hash of the original message (which may otherwise
           * be too long), and then calling {toEthSignedMessageHash} on it.
           */
          function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
              (address recovered, RecoverError error) = tryRecover(hash, signature);
              _throwError(error);
              return recovered;
          }
          /**
           * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
           *
           * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
           *
           * _Available since v4.3._
           */
          function tryRecover(
              bytes32 hash,
              bytes32 r,
              bytes32 vs
          ) internal pure returns (address, RecoverError) {
              bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
              uint8 v = uint8((uint256(vs) >> 255) + 27);
              return tryRecover(hash, v, r, s);
          }
          /**
           * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
           *
           * _Available since v4.2._
           */
          function recover(
              bytes32 hash,
              bytes32 r,
              bytes32 vs
          ) internal pure returns (address) {
              (address recovered, RecoverError error) = tryRecover(hash, r, vs);
              _throwError(error);
              return recovered;
          }
          /**
           * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
           * `r` and `s` signature fields separately.
           *
           * _Available since v4.3._
           */
          function tryRecover(
              bytes32 hash,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) internal pure returns (address, RecoverError) {
              // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
              // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
              // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
              // signatures from current libraries generate a unique signature with an s-value in the lower half order.
              //
              // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
              // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
              // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
              // these malleable signatures as well.
              if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                  return (address(0), RecoverError.InvalidSignatureS);
              }
              if (v != 27 && v != 28) {
                  return (address(0), RecoverError.InvalidSignatureV);
              }
              // If the signature is valid (and not malleable), return the signer address
              address signer = ecrecover(hash, v, r, s);
              if (signer == address(0)) {
                  return (address(0), RecoverError.InvalidSignature);
              }
              return (signer, RecoverError.NoError);
          }
          /**
           * @dev Overload of {ECDSA-recover} that receives the `v`,
           * `r` and `s` signature fields separately.
           */
          function recover(
              bytes32 hash,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) internal pure returns (address) {
              (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
              _throwError(error);
              return recovered;
          }
          /**
           * @dev Returns an Ethereum Signed Message, created from a `hash`. This
           * produces hash corresponding to the one signed with the
           * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
           * JSON-RPC method as part of EIP-191.
           *
           * See {recover}.
           */
          function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
              // 32 is the length in bytes of hash,
              // enforced by the type signature above
              return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
      32", hash));
          }
          /**
           * @dev Returns an Ethereum Signed Message, created from `s`. This
           * produces hash corresponding to the one signed with the
           * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
           * JSON-RPC method as part of EIP-191.
           *
           * See {recover}.
           */
          function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
              return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
      ", Strings.toString(s.length), s));
          }
          /**
           * @dev Returns an Ethereum Signed Typed Data, created from a
           * `domainSeparator` and a `structHash`. This produces hash corresponding
           * to the one signed with the
           * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
           * JSON-RPC method as part of EIP-712.
           *
           * See {recover}.
           */
          function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
              return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      pragma abicoder v2;
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
      import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
      import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
      import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
      import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
      import "@openzeppelin/contracts/utils/Address.sol";
      import "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
      import "@openzeppelin/contracts/utils/math/SafeMath.sol";
      import "./interfaces/IFactory.sol";
      abstract contract ExchangeCore is ReentrancyGuard, EIP712 {
          using SafeERC20 for IERC20;
          error InvalidOrder();
          error InvalidTarget();
          error InvalidOrderParameters();
          error NonMatchableOrders();
          error NotAuthorized();
          error InvalidCollection();
          error InvalidSender();
          error AlreadyApproved();
          bytes32 private immutable _ORDER_TYPEHASH;
          /* Inverse basis point. */
          uint256 public constant INVERSE_BASIS_POINT = 10000;
          /* The token used to pay exchange fees. */
          IERC20 public exchangeToken;
          /* Cancelled / finalized orders, by hash. */
          mapping(bytes32 => bool) public cancelledOrFinalized;
          /* Orders verified by on-chain approval (alternative to ECDSA signatures so that smart contracts can place orders directly). */
          /* Note that the maker's nonce at the time of approval **plus one** is stored in the mapping. */
          mapping(bytes32 => uint256) private _approvedOrdersByNonce;
          /* Track per-maker nonces that can be incremented by the maker to cancel orders in bulk. */
          // The current nonce for the maker represents the only valid nonce that can be signed by the maker
          // If a signature was signed with a nonce that's different from the one stored in nonces, it
          // will fail validation.
          mapping(address => uint256) public nonces;
          /* List of allowed collections */
          mapping(address => bool) public allowedCollections;
          /* Recipient of protocol fees. */
          address public protocolFeeRecipient;
          /* Recipient of mint fees. */
          mapping(address => address) internal mintFeeRecipient;
          enum OrderSide {
              Buy,
              Sell
          }
          enum CollectionType {
              ERC721,
              ERC1155
          }
          /* An order on the exchange. */
          struct Order {
              /* Order maker address. */
              address maker;
              /* Order taker address, if specified. */
              address taker;
              /* Maker protocol fee of the order, unused for taker order. */
              uint256 makerProtocolFee;
              /* Taker protocol fee of the order, or maximum taker fee for a taker order. */
              uint256 takerProtocolFee;
              /* Order fee recipient or zero address for taker order. */
              address feeRecipient;
              /* Side (buy/sell). */
              OrderSide side;
              /* Target collection type. */
              CollectionType collectionType;
              /* Factory for mint. */
              address mintFactory;
              /* Target collection. */
              address collection;
              /* TokenIds to transfer. */
              uint256[] tokenIds;
              /* Amount of tokenIds to transfer (ERC1155). */
              uint256[] amounts;
              /* Token used to pay for the order, or the zero-address as a sentinel value for Ether. */
              address paymentToken;
              /* Base price of the order (in paymentTokens). */
              uint256 basePrice;
              /* Extra parameter - reserved */
              uint256 extra;
              /* Listing timestamp. */
              uint256 listingTime;
              /* Expiration timestamp - 0 for no expiry. */
              uint256 expirationTime;
              /* Order salt, used to prevent duplicate hashes. */
              uint256 salt;
              /* NOTE: uint nonce is an additional component of the order but is read from storage */
          }
          event OrderApproved(bytes32 indexed hash, Order order, bool orderbookInclusionDesired);
          event OrderCancelled(bytes32 indexed hash);
          event OrdersMatched(
              bytes32 buyHash,
              bytes32 sellHash,
              address indexed maker,
              address indexed taker,
              uint256 price,
              bytes32 indexed metadata
          );
          event NonceIncremented(address indexed maker, uint256 newNonce);
          constructor() {
              bytes32 typeHash = keccak256(
                  "Order(address maker,address taker,uint256 makerProtocolFee,uint256 takerProtocolFee,address feeRecipient,uint8 side,uint8 collectionType,address mintFactory,address collection,uint256[] tokenIds,uint256[] amounts,address paymentToken,uint256 basePrice,uint256 extra,uint256 listingTime,uint256 expirationTime,uint256 salt,uint256 nonce)"
              );
              _ORDER_TYPEHASH = typeHash;
          }
          /**
           * Increment a particular maker's nonce, thereby invalidating all orders that were not signed
           * with the original nonce.
           */
          function incrementNonce() external {
              uint256 newNonce = ++nonces[msg.sender];
              emit NonceIncremented(msg.sender, newNonce);
          }
          /**
           * @dev Transfer tokens
           * @param token Token to transfer
           * @param from Address to charge fees
           * @param to Address to receive fees
           * @param amount Amount of protocol tokens to charge
           */
          function transferTokens(
              address token,
              address from,
              address to,
              uint256 amount
          ) internal {
              if (amount > 0) {
                  IERC20(token).safeTransferFrom(from, to, amount);
              }
          }
          /**
           * @dev Hash an order, returning the canonical EIP-712 order hash without the domain separator
           * @param order Order to hash
           * @param nonce maker nonce to hash
           * @return hash Hash of order
           */
          function hashOrder(Order memory order, uint256 nonce) internal view returns (bytes32) {
              return
                  keccak256(
                      bytes.concat(
                          abi.encode(_ORDER_TYPEHASH),
                          abi.encode(
                              order.maker,
                              order.taker,
                              order.makerProtocolFee,
                              order.takerProtocolFee,
                              order.feeRecipient,
                              order.side
                          ),
                          abi.encode(order.collectionType, order.mintFactory, order.collection),
                          keccak256(abi.encodePacked(order.tokenIds)),
                          keccak256(abi.encodePacked(order.amounts)),
                          abi.encode(
                              order.paymentToken,
                              order.basePrice,
                              order.extra,
                              order.listingTime,
                              order.expirationTime,
                              order.salt
                          ),
                          abi.encode(nonce)
                      )
                  );
          }
          /**
           * @dev Hash an order, returning the hash that a client must sign via EIP-712 including the message prefix
           * @param order Order to hash
           * @param nonce Nonce to hash
           * @return Hash of message prefix and order hash per Ethereum format
           */
          function hashToSign(Order memory order, uint256 nonce) internal view returns (bytes32) {
              return _hashTypedDataV4(hashOrder(order, nonce));
          }
          /**
           * @dev Assert an order is valid and return its hash
           * @param order Order to validate
           * @param nonce Nonce to validate
           * @param signature ECDSA signature
           */
          function requireValidOrder(
              Order memory order,
              bytes memory signature,
              uint256 nonce
          ) internal view returns (bytes32) {
              bytes32 hash = hashToSign(order, nonce);
              if (!validateOrder(hash, order, signature)) revert InvalidOrder();
              return hash;
          }
          /**
           * @dev Validate order parameters (does *not* check signature validity)
           * @param order Order to validate
           */
          function validateOrderParameters(Order memory order) internal view returns (bool) {
              /* Order must have a maker. */
              if (order.maker == address(0)) {
                  return false;
              }
              if (order.tokenIds.length == 0 || order.tokenIds.length != order.amounts.length) {
                  return false;
              }
              if (order.mintFactory != address(0) && !allowedCollections[order.mintFactory]) {
                  return false;
              }
              if (order.collection == address(0) || !allowedCollections[order.collection]) {
                  return false;
              }
              return true;
          }
          /**
           * @dev Validate a provided previously approved / signed order, hash, and signature.
           * @param hash Order hash (already calculated, passed to avoid recalculation)
           * @param order Order to validate
           * @param signature ECDSA signature
           */
          function validateOrder(
              bytes32 hash,
              Order memory order,
              bytes memory signature
          ) internal view returns (bool) {
              /* Not done in an if-conditional to prevent unnecessary ecrecover evaluation, which seems to happen even though it should short-circuit. */
              /* Order must have valid parameters. */
              if (!validateOrderParameters(order)) {
                  return false;
              }
              /* Order must have not been canceled or already filled. */
              if (cancelledOrFinalized[hash]) {
                  return false;
              }
              /* Return true if order has been previously approved with the current nonce */
              uint256 approvedOrderNoncePlusOne = _approvedOrdersByNonce[hash];
              if (approvedOrderNoncePlusOne != 0) {
                  return approvedOrderNoncePlusOne == nonces[order.maker] + 1;
              }
              /* validate signature. */
              return SignatureChecker.isValidSignatureNow(order.maker, hash, signature);
          }
          /**
           * @dev Return whether or not an order can be settled
           * @dev Precondition: parameters have passed validateParameters
           * @param listingTime Order listing time
           * @param expirationTime Order expiration time
           */
          function canSettleOrder(uint256 listingTime, uint256 expirationTime) internal view returns (bool) {
              return (listingTime < block.timestamp) && (expirationTime == 0 || block.timestamp < expirationTime);
          }
          /**
           * @dev Determine if an order has been approved. Note that the order may not still
           * be valid in cases where the maker's nonce has been incremented.
           * @param hash Hash of the order
           * @return approved whether or not the order was approved.
           */
          function approvedOrders(bytes32 hash) public view returns (bool approved) {
              return _approvedOrdersByNonce[hash] != 0;
          }
          /**
           * @dev Approve an order and optionally mark it for orderbook inclusion. Must be called by the maker of the order
           * @param order Order to approve
           * @param orderbookInclusionDesired Whether orderbook providers should include the order in their orderbooks
           */
          function approveOrder(Order memory order, bool orderbookInclusionDesired) internal {
              /* CHECKS */
              /* Assert sender is authorized to approve order. */
              if (msg.sender != order.maker) revert InvalidSender();
              /* Calculate order hash. */
              bytes32 hash = hashToSign(order, nonces[order.maker]);
              /* Assert order has not already been approved. */
              if (_approvedOrdersByNonce[hash] != 0) revert AlreadyApproved();
              /* EFFECTS */
              /* Mark order as approved. */
              _approvedOrdersByNonce[hash] = nonces[order.maker] + 1;
              emit OrderApproved(hash, order, orderbookInclusionDesired);
          }
          /**
           * @dev Cancel an order, preventing it from being matched. Must be called by the maker of the order
           * @param order Order to cancel
           * @param nonce Nonce to cancel
           * @param signature ECDSA signature
           */
          function cancelOrder(
              Order memory order,
              bytes memory signature,
              uint256 nonce
          ) internal {
              /* CHECKS */
              /* Calculate order hash. */
              bytes32 hash = requireValidOrder(order, signature, nonce);
              /* Assert sender is authorized to cancel order. */
              if (msg.sender != order.maker) revert NotAuthorized();
              /* EFFECTS */
              /* Mark order as cancelled, preventing it from being matched. */
              cancelledOrFinalized[hash] = true;
              /* Log cancel event. */
              emit OrderCancelled(hash);
          }
          /**
           * @dev Calculate the price two orders would match at, if in fact they would match (otherwise fail)
           * @param buy Buy-side order
           * @param sell Sell-side order
           * @return Match price
           */
          function calculateMatchPrice(Order memory buy, Order memory sell) internal pure returns (uint256) {
              /* Calculate sell price. */
              uint256 sellPrice = sell.basePrice;
              /* Calculate buy price. */
              uint256 buyPrice = buy.basePrice;
              /* Require price cross. */
              require(buyPrice >= sellPrice);
              /* Maker/taker priority. */
              return sell.feeRecipient != address(0) ? sellPrice : buyPrice;
          }
          /**
           * @dev Execute all ERC20 token / Ether transfers associated with an order match (fees and buyer => seller transfer)
           * @param buy Buy-side order
           * @param sell Sell-side order
           */
          function executeFundsTransfer(Order memory buy, Order memory sell) internal returns (uint256) {
              /* Only payable in the special case of unwrapped Ether. */
              if (sell.paymentToken != address(0)) {
                  require(msg.value == 0);
              }
              /* Calculate match price. */
              uint256 price = calculateMatchPrice(buy, sell);
              address paymentRecipient = sell.mintFactory != address(0) ? getMintRecipient(sell.collection) : sell.maker;
              /* If paying using a token (not Ether), transfer tokens. This is done prior to fee payments to that a seller will have tokens before being charged fees. */
              if (price > 0 && sell.paymentToken != address(0)) {
                  transferTokens(sell.paymentToken, buy.maker, paymentRecipient, price);
              }
              /* Amount that will be received by seller (for Ether). */
              uint256 receiveAmount = price;
              /* Amount that must be sent by buyer (for Ether). */
              uint256 requiredAmount = price;
              uint256 makerProtocolFee;
              uint256 takerProtocolFee;
              /* Determine maker/taker and charge fees accordingly. */
              if (sell.feeRecipient != address(0)) {
                  /* Sell-side order is maker. */
                  /* Assert taker fee is less than or equal to maximum fee specified by buyer. */
                  require(sell.takerProtocolFee <= buy.takerProtocolFee);
                  /* Maker fees are deducted from the token amount that the maker receives. Taker fees are extra tokens that must be paid by the taker. */
                  if (sell.makerProtocolFee > 0) {
                      makerProtocolFee = SafeMath.div(SafeMath.mul(sell.makerProtocolFee, price), INVERSE_BASIS_POINT);
                      if (sell.paymentToken == address(0)) {
                          receiveAmount = SafeMath.sub(receiveAmount, makerProtocolFee);
                          payable(protocolFeeRecipient).transfer(makerProtocolFee);
                      } else {
                          transferTokens(sell.paymentToken, sell.maker, protocolFeeRecipient, makerProtocolFee);
                      }
                  }
                  if (sell.takerProtocolFee > 0) {
                      takerProtocolFee = SafeMath.div(SafeMath.mul(sell.takerProtocolFee, price), INVERSE_BASIS_POINT);
                      if (sell.paymentToken == address(0)) {
                          requiredAmount = SafeMath.add(requiredAmount, takerProtocolFee);
                          payable(protocolFeeRecipient).transfer(takerProtocolFee);
                      } else {
                          transferTokens(sell.paymentToken, buy.maker, protocolFeeRecipient, takerProtocolFee);
                      }
                  }
              } else {
                  /* Buy-side order is maker. */
                  /* The Exchange does not escrow Ether, so direct Ether can only be used to with sell-side maker / buy-side taker orders. */
                  require(sell.paymentToken != address(0));
                  /* Assert taker fee is less than or equal to maximum fee specified by seller. */
                  require(buy.takerProtocolFee <= sell.takerProtocolFee);
                  if (buy.makerProtocolFee > 0) {
                      makerProtocolFee = SafeMath.div(SafeMath.mul(buy.makerProtocolFee, price), INVERSE_BASIS_POINT);
                      transferTokens(sell.paymentToken, buy.maker, protocolFeeRecipient, makerProtocolFee);
                  }
                  if (buy.takerProtocolFee > 0) {
                      takerProtocolFee = SafeMath.div(SafeMath.mul(buy.takerProtocolFee, price), INVERSE_BASIS_POINT);
                      transferTokens(sell.paymentToken, sell.maker, protocolFeeRecipient, takerProtocolFee);
                  }
              }
              if (sell.paymentToken == address(0)) {
                  /* Special-case Ether, order must be matched by buyer. */
                  require(msg.value >= requiredAmount);
                  payable(paymentRecipient).transfer(receiveAmount);
                  /* Allow overshoot for variable-price auctions, refund difference. */
                  uint256 diff = SafeMath.sub(msg.value, requiredAmount);
                  if (diff > 0) {
                      payable(buy.maker).transfer(diff);
                  }
              }
              /* This contract should never hold Ether, however, we cannot assert this, since it is impossible to prevent anyone from sending Ether e.g. with selfdestruct. */
              return price;
          }
          /**
           * @dev Execute all ERC721/ERC1155 token transfers associated with an order match
           * @param buy Buy-side order
           * @param sell Sell-side order
           */
          function executeTokensTransfer(Order memory buy, Order memory sell) internal {
              if (sell.collectionType == CollectionType.ERC721) {
                  if (sell.mintFactory != address(0)) {
                      if (sell.tokenIds.length == 1) {
                          IERC721Factory(sell.mintFactory).mint(buy.maker, sell.tokenIds[0], "");
                      } else {
                          IERC721Factory(sell.mintFactory).mintBatch(buy.maker, sell.tokenIds, "");
                      }
                  } else {
                      for (uint256 i = 0; i < sell.tokenIds.length; i++) {
                          require(sell.amounts[i] == 1, "Invalid amount");
                          IERC721(sell.collection).safeTransferFrom(sell.maker, buy.maker, sell.tokenIds[i], "");
                      }
                  }
              } else if (sell.collectionType == CollectionType.ERC1155) {
                  if (sell.mintFactory != address(0)) {
                      if (sell.tokenIds.length == 1) {
                          IERC1155Factory(sell.mintFactory).mint(buy.maker, sell.tokenIds[0], sell.amounts[0], "");
                      } else {
                          IERC1155Factory(sell.mintFactory).mintBatch(buy.maker, sell.tokenIds, sell.amounts, "");
                      }
                  } else {
                      if (sell.tokenIds.length == 1) {
                          IERC1155(sell.collection).safeTransferFrom(
                              sell.maker,
                              buy.maker,
                              sell.tokenIds[0],
                              sell.amounts[0],
                              ""
                          );
                      } else {
                          IERC1155(sell.collection).safeBatchTransferFrom(
                              sell.maker,
                              buy.maker,
                              sell.tokenIds,
                              sell.amounts,
                              ""
                          );
                      }
                  }
              }
          }
          function uintArrayMatch(uint256[] memory a, uint256[] memory b) internal pure returns (bool) {
              if (a.length != b.length) return false;
              for (uint256 i = 0; i < a.length; i++) {
                  if (a[i] != b[i]) return false;
              }
              return true;
          }
          /**
           * @dev Return whether or not two orders can be matched with each other by basic parameters (does not check order signatures / calldata or perform static calls)
           * @param buy Buy-side order
           * @param sell Sell-side order
           * @return Whether or not the two orders can be matched
           */
          function ordersCanMatch(Order memory buy, Order memory sell) internal view returns (bool) {
              return (/* Must be opposite-side. */
              (buy.side == OrderSide.Buy && sell.side == OrderSide.Sell) &&
                  /* Must match tokens. */
                  (buy.tokenIds.length > 0 && uintArrayMatch(buy.tokenIds, sell.tokenIds)) &&
                  /* Must match amounts. */
                  (sell.amounts.length == 0 || uintArrayMatch(buy.amounts, sell.amounts)) &&
                  /* Must use same payment token. */
                  (buy.paymentToken == sell.paymentToken) &&
                  /* Must match maker/taker addresses. */
                  (sell.taker == address(0) || sell.taker == buy.maker) &&
                  (buy.taker == address(0) || buy.taker == sell.maker) &&
                  /* One must be maker and the other must be taker (no bool XOR in Solidity). */
                  ((sell.feeRecipient == address(0) && buy.feeRecipient != address(0)) ||
                      (sell.feeRecipient != address(0) && buy.feeRecipient == address(0))) &&
                  /* Must match mint factory. */
                  (buy.mintFactory == sell.mintFactory) &&
                  /* Must match target. */
                  (buy.collection == sell.collection) &&
                  /* Buy-side order must be settleable. */
                  canSettleOrder(buy.listingTime, buy.expirationTime) &&
                  /* Sell-side order must be settleable. */
                  canSettleOrder(sell.listingTime, sell.expirationTime));
          }
          function canMint(
              address minter,
              address factory,
              address collection
          ) internal view returns (bool) {
              bool allowed = IFactory(factory).canMint(collection, minter);
              return allowed;
          }
          /**
           * @dev Atomically match two orders, ensuring validity of the match, and execute all associated state transitions. Protected against reentrancy by a contract-global lock.
           * @param buy Buy-side order
           * @param buySig Buy-side order signature
           * @param sell Sell-side order
           * @param sellSig Sell-side order signature
           */
          function atomicMatch(
              Order memory buy,
              bytes memory buySig,
              Order memory sell,
              bytes memory sellSig,
              bytes32 metadata
          ) internal nonReentrant {
              /* CHECKS */
              /* Ensure buy order validity and calculate hash if necessary. */
              bytes32 buyHash;
              if (buy.maker == msg.sender) {
                  if (!validateOrderParameters(buy)) revert InvalidOrderParameters();
              } else {
                  buyHash = _requireValidOrderWithNonce(buy, buySig);
              }
              /* Ensure sell order validity and calculate hash if necessary. */
              bytes32 sellHash;
              if (sell.maker == msg.sender) {
                  if (!validateOrderParameters(sell)) revert InvalidOrderParameters();
              } else {
                  sellHash = _requireValidOrderWithNonce(sell, sellSig);
              }
              /* Must be matchable. */
              if (!ordersCanMatch(buy, sell)) revert NonMatchableOrders();
              address target = sell.mintFactory;
              if (target != address(0)) {
                  /* Minter must be allowed */
                  if (!canMint(sell.maker, sell.mintFactory, sell.collection)) revert NotAuthorized();
              } else {
                  target = sell.collection;
              }
              /* Target must exist (prevent malicious selfdestructs just prior to order settlement). */
              if (!Address.isContract(target)) revert InvalidTarget();
              /* EFFECTS */
              /* Mark previously signed or approved orders as finalized. */
              if (msg.sender != buy.maker) {
                  cancelledOrFinalized[buyHash] = true;
              }
              if (msg.sender != sell.maker) {
                  cancelledOrFinalized[sellHash] = true;
              }
              /* INTERACTIONS */
              /* Execute funds transfer and pay fees. */
              uint256 price = executeFundsTransfer(buy, sell);
              /* Execute tokens transfers. */
              executeTokensTransfer(buy, sell);
              /* Log match event. */
              emit OrdersMatched(
                  buyHash,
                  sellHash,
                  sell.feeRecipient != address(0) ? sell.maker : buy.maker,
                  sell.feeRecipient != address(0) ? buy.maker : sell.maker,
                  price,
                  metadata
              );
          }
          function _requireValidOrderWithNonce(Order memory order, bytes memory signature) internal view returns (bytes32) {
              return requireValidOrder(order, signature, nonces[order.maker]);
          }
          function getMintRecipient(address collection) public view returns (address) {
              address recipient = mintFeeRecipient[collection];
              if (recipient != address(0)) return recipient;
              recipient = mintFeeRecipient[address(0)];
              if (recipient != address(0)) return recipient;
              return protocolFeeRecipient;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/Context.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 Context {
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)
      pragma solidity ^0.8.1;
      /**
       * @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
           * ====
           *
           * [IMPORTANT]
           * ====
           * You shouldn't rely on `isContract` to protect against flash loan attacks!
           *
           * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
           * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
           * constructor.
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies on extcodesize/address.code.length, which returns 0
              // for contracts in construction, since the code is only stored at the end
              // of the constructor execution.
              return account.code.length > 0;
          }
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
              (bool success, ) = recipient.call{value: amount}("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain `call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCall(target, data, "Address: low-level call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              require(isContract(target), "Address: call to non-contract");
              (bool success, bytes memory returndata) = target.call{value: value}(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal view returns (bytes memory) {
              require(isContract(target), "Address: static call to non-contract");
              (bool success, bytes memory returndata) = target.staticcall(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionDelegateCall(target, data, "Address: low-level delegate call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(isContract(target), "Address: delegate call to non-contract");
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
           * revert reason using the provided one.
           *
           * _Available since v4.3._
           */
          function verifyCallResult(
              bool success,
              bytes memory returndata,
              string memory errorMessage
          ) internal pure returns (bytes memory) {
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev String operations.
       */
      library Strings {
          bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
          /**
           * @dev Converts a `uint256` to its ASCII `string` decimal representation.
           */
          function toString(uint256 value) internal pure returns (string memory) {
              // Inspired by OraclizeAPI's implementation - MIT licence
              // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
              if (value == 0) {
                  return "0";
              }
              uint256 temp = value;
              uint256 digits;
              while (temp != 0) {
                  digits++;
                  temp /= 10;
              }
              bytes memory buffer = new bytes(digits);
              while (value != 0) {
                  digits -= 1;
                  buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                  value /= 10;
              }
              return string(buffer);
          }
          /**
           * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
           */
          function toHexString(uint256 value) internal pure returns (string memory) {
              if (value == 0) {
                  return "0x00";
              }
              uint256 temp = value;
              uint256 length = 0;
              while (temp != 0) {
                  length++;
                  temp >>= 8;
              }
              return toHexString(value, length);
          }
          /**
           * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
           */
          function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
              bytes memory buffer = new bytes(2 * length + 2);
              buffer[0] = "0";
              buffer[1] = "x";
              for (uint256 i = 2 * length + 1; i > 1; --i) {
                  buffer[i] = _HEX_SYMBOLS[value & 0xf];
                  value >>= 4;
              }
              require(value == 0, "Strings: hex length insufficient");
              return string(buffer);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721.sol)
      pragma solidity ^0.8.0;
      import "../../utils/introspection/IERC165.sol";
      /**
       * @dev Required interface of an ERC721 compliant contract.
       */
      interface IERC721 is IERC165 {
          /**
           * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
           */
          event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
          /**
           * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
           */
          event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
          /**
           * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
           */
          event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
          /**
           * @dev Returns the number of tokens in ``owner``'s account.
           */
          function balanceOf(address owner) external view returns (uint256 balance);
          /**
           * @dev Returns the owner of the `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function ownerOf(uint256 tokenId) external view returns (address owner);
          /**
           * @dev Safely transfers `tokenId` token from `from` to `to`.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must exist and be owned by `from`.
           * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
           * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
           *
           * Emits a {Transfer} event.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 tokenId,
              bytes calldata data
          ) external;
          /**
           * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
           * are aware of the ERC721 protocol to prevent tokens from being forever locked.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must exist and be owned by `from`.
           * - If the caller is not `from`, it must 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 Approve or remove `operator` as an operator for the caller.
           * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
           *
           * Requirements:
           *
           * - The `operator` cannot be the caller.
           *
           * Emits an {ApprovalForAll} event.
           */
          function setApprovalForAll(address operator, bool _approved) external;
          /**
           * @dev Returns the account approved for `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function getApproved(uint256 tokenId) external view returns (address operator);
          /**
           * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
           *
           * See {setApprovalForAll}
           */
          function isApprovedForAll(address owner, address operator) external view returns (bool);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (token/ERC1155/IERC1155.sol)
      pragma solidity ^0.8.0;
      import "../../utils/introspection/IERC165.sol";
      /**
       * @dev Required interface of an ERC1155 compliant contract, as defined in the
       * https://eips.ethereum.org/EIPS/eip-1155[EIP].
       *
       * _Available since v3.1._
       */
      interface IERC1155 is IERC165 {
          /**
           * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
           */
          event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
          /**
           * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
           * transfers.
           */
          event TransferBatch(
              address indexed operator,
              address indexed from,
              address indexed to,
              uint256[] ids,
              uint256[] values
          );
          /**
           * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
           * `approved`.
           */
          event ApprovalForAll(address indexed account, address indexed operator, bool approved);
          /**
           * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
           *
           * If an {URI} event was emitted for `id`, the standard
           * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
           * returned by {IERC1155MetadataURI-uri}.
           */
          event URI(string value, uint256 indexed id);
          /**
           * @dev Returns the amount of tokens of token type `id` owned by `account`.
           *
           * Requirements:
           *
           * - `account` cannot be the zero address.
           */
          function balanceOf(address account, uint256 id) external view returns (uint256);
          /**
           * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
           *
           * Requirements:
           *
           * - `accounts` and `ids` must have the same length.
           */
          function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
              external
              view
              returns (uint256[] memory);
          /**
           * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
           *
           * Emits an {ApprovalForAll} event.
           *
           * Requirements:
           *
           * - `operator` cannot be the caller.
           */
          function setApprovalForAll(address operator, bool approved) external;
          /**
           * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
           *
           * See {setApprovalForAll}.
           */
          function isApprovedForAll(address account, address operator) external view returns (bool);
          /**
           * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
           *
           * Emits a {TransferSingle} event.
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
           * - `from` must have a balance of tokens of type `id` of at least `amount`.
           * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
           * acceptance magic value.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 id,
              uint256 amount,
              bytes calldata data
          ) external;
          /**
           * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
           *
           * Emits a {TransferBatch} event.
           *
           * Requirements:
           *
           * - `ids` and `amounts` must have the same length.
           * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
           * acceptance magic value.
           */
          function safeBatchTransferFrom(
              address from,
              address to,
              uint256[] calldata ids,
              uint256[] calldata amounts,
              bytes calldata data
          ) external;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.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 ReentrancyGuard {
          // 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;
          constructor() {
              _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 making 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;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/SignatureChecker.sol)
      pragma solidity ^0.8.0;
      import "./ECDSA.sol";
      import "../Address.sol";
      import "../../interfaces/IERC1271.sol";
      /**
       * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
       * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
       * Argent and Gnosis Safe.
       *
       * _Available since v4.1._
       */
      library SignatureChecker {
          /**
           * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
           * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
           *
           * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
           * change through time. It could return true at block N and false at block N+1 (or the opposite).
           */
          function isValidSignatureNow(
              address signer,
              bytes32 hash,
              bytes memory signature
          ) internal view returns (bool) {
              (address recovered, ECDSA.RecoverError error) = ECDSA.tryRecover(hash, signature);
              if (error == ECDSA.RecoverError.NoError && recovered == signer) {
                  return true;
              }
              (bool success, bytes memory result) = signer.staticcall(
                  abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature)
              );
              return (success && result.length == 32 && abi.decode(result, (bytes4)) == IERC1271.isValidSignature.selector);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.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 generally not needed starting with Solidity 0.8, since the compiler
       * now has built in overflow checking.
       */
      library SafeMath {
          /**
           * @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 subtraction 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;
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      interface IFactory {
          function canMint(address collection, address account) external view returns (bool);
      }
      interface IERC721Factory {
          function mint(
              address to,
              uint256 id,
              bytes memory data
          ) external;
          function mintBatch(
              address to,
              uint256[] memory ids,
              bytes memory data
          ) external;
      }
      interface IERC1155Factory {
          function mint(
              address to,
              uint256 id,
              uint256 amount,
              bytes memory data
          ) external;
          function mintBatch(
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) external;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC165 standard, as defined in the
       * https://eips.ethereum.org/EIPS/eip-165[EIP].
       *
       * Implementers can declare support of contract interfaces, which can then be
       * queried by others ({ERC165Checker}).
       *
       * For an implementation, see {ERC165}.
       */
      interface IERC165 {
          /**
           * @dev Returns true if this contract implements the interface defined by
           * `interfaceId`. See the corresponding
           * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
           * to learn more about how these ids are created.
           *
           * This function call must use less than 30 000 gas.
           */
          function supportsInterface(bytes4 interfaceId) external view returns (bool);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC1271 standard signature validation method for
       * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
       *
       * _Available since v4.1._
       */
      interface IERC1271 {
          /**
           * @dev Should return whether the signature provided is valid for the provided data
           * @param hash      Hash of the data to be signed
           * @param signature Signature byte array associated with _data
           */
          function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
      }
      

      File 2 of 3: KompeteGameAsset
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "@openzeppelin/contracts/utils/Context.sol";
      import "@openzeppelin/contracts/access/AccessControl.sol";
      import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
      import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Burnable.sol";
      import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Pausable.sol";
      import "./extensions/ERC1155CapSupply.sol";
      import "./extensions/ERC1155Metadata.sol";
      import "./libraries/PauserAccess.sol";
      import "./interfaces/IMintableERC1155.sol";
      import "./interfaces/IProxyRegistry.sol";
      contract KompeteGameAsset is
          Context,
          AccessControl,
          PauserAccess,
          ERC1155Metadata,
          ERC1155CapSupply,
          ERC1155Burnable,
          ERC1155Pausable,
          IMintableERC1155
      {
          bytes32 public constant FACTORY_ROLE = keccak256("FACTORY_ROLE");
          address public registry;
          string public contractURI;
          constructor(
              address _registry,
              string memory _uri,
              string memory _name
          ) ERC1155(_uri) ERC1155Metadata(_name) {
              _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
              registry = _registry;
          }
          modifier onlyAdmins() {
              require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "Game: admin role required");
              _;
          }
          modifier onlyFactory() {
              require(hasRole(FACTORY_ROLE, _msgSender()), "Game: factory role required");
              _;
          }
          /**
           * @dev Creates `amount` new tokens for `to`, of token type `id`.
           *
           * See {ERC1155-_mint}.
           *
           * Requirements:
           *
           * - the caller must have the `FACTORY_ROLE`.
           */
          function mint(
              address to,
              uint256 id,
              uint256 amount,
              bytes memory data
          ) external override onlyFactory {
              _mint(to, id, amount, data);
          }
          /**
           * @dev Batched variant of {mint}.
           */
          function mintBatch(
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) external override onlyFactory {
              _mintBatch(to, ids, amounts, data);
          }
          /**
           * @dev Set the max supply for a tokenId
           */
          function setMaxSupply(
              uint256 id,
              uint256 max,
              bool freeze
          ) external onlyFactory {
              _setMaxSupply(id, max, freeze);
          }
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControl, ERC1155) returns (bool) {
              return super.supportsInterface(interfaceId);
          }
          function _beforeTokenTransfer(
              address operator,
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual override(ERC1155, ERC1155Pausable, ERC1155CapSupply) {
              super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
          }
          function isApprovedForAll(address account, address operator) public view override returns (bool) {
              // Whitelist proxy contract for easy trading.
              if (registry != address(0)) {
                  IProxyRegistry proxyRegistry = IProxyRegistry(registry);
                  if (address(proxyRegistry.proxies(account)) == operator) {
                      return true;
                  }
              }
              return super.isApprovedForAll(account, operator);
          }
          function setProxyRegistry(address _registry) external onlyAdmins {
              require(registry == address(0), "Game: registry already set");
              registry = _registry;
          }
          function setURI(string memory newuri) external onlyAdmins {
              require(bytes(newuri).length > 0, "Game: empty uri");
              _setURI(newuri);
          }
          function setContractURI(string memory newuri) external onlyAdmins {
              require(bytes(newuri).length > 0, "Game: empty uri");
              contractURI = newuri;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/Context.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 Context {
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (access/AccessControl.sol)
      pragma solidity ^0.8.0;
      import "./IAccessControl.sol";
      import "../utils/Context.sol";
      import "../utils/Strings.sol";
      import "../utils/introspection/ERC165.sol";
      /**
       * @dev Contract module that allows children to implement role-based access
       * control mechanisms. This is a lightweight version that doesn't allow enumerating role
       * members except through off-chain means by accessing the contract event logs. Some
       * applications may benefit from on-chain enumerability, for those cases see
       * {AccessControlEnumerable}.
       *
       * Roles are referred to by their `bytes32` identifier. These should be exposed
       * in the external API and be unique. The best way to achieve this is by
       * using `public constant` hash digests:
       *
       * ```
       * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
       * ```
       *
       * Roles can be used to represent a set of permissions. To restrict access to a
       * function call, use {hasRole}:
       *
       * ```
       * function foo() public {
       *     require(hasRole(MY_ROLE, msg.sender));
       *     ...
       * }
       * ```
       *
       * Roles can be granted and revoked dynamically via the {grantRole} and
       * {revokeRole} functions. Each role has an associated admin role, and only
       * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
       *
       * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
       * that only accounts with this role will be able to grant or revoke other
       * roles. More complex role relationships can be created by using
       * {_setRoleAdmin}.
       *
       * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
       * grant and revoke this role. Extra precautions should be taken to secure
       * accounts that have been granted it.
       */
      abstract contract AccessControl is Context, IAccessControl, ERC165 {
          struct RoleData {
              mapping(address => bool) members;
              bytes32 adminRole;
          }
          mapping(bytes32 => RoleData) private _roles;
          bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
          /**
           * @dev Modifier that checks that an account has a specific role. Reverts
           * with a standardized message including the required role.
           *
           * The format of the revert reason is given by the following regular expression:
           *
           *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
           *
           * _Available since v4.1._
           */
          modifier onlyRole(bytes32 role) {
              _checkRole(role);
              _;
          }
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
              return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
          }
          /**
           * @dev Returns `true` if `account` has been granted `role`.
           */
          function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
              return _roles[role].members[account];
          }
          /**
           * @dev Revert with a standard message if `_msgSender()` is missing `role`.
           * Overriding this function changes the behavior of the {onlyRole} modifier.
           *
           * Format of the revert message is described in {_checkRole}.
           *
           * _Available since v4.6._
           */
          function _checkRole(bytes32 role) internal view virtual {
              _checkRole(role, _msgSender());
          }
          /**
           * @dev Revert with a standard message if `account` is missing `role`.
           *
           * The format of the revert reason is given by the following regular expression:
           *
           *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
           */
          function _checkRole(bytes32 role, address account) internal view virtual {
              if (!hasRole(role, account)) {
                  revert(
                      string(
                          abi.encodePacked(
                              "AccessControl: account ",
                              Strings.toHexString(uint160(account), 20),
                              " is missing role ",
                              Strings.toHexString(uint256(role), 32)
                          )
                      )
                  );
              }
          }
          /**
           * @dev Returns the admin role that controls `role`. See {grantRole} and
           * {revokeRole}.
           *
           * To change a role's admin, use {_setRoleAdmin}.
           */
          function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
              return _roles[role].adminRole;
          }
          /**
           * @dev Grants `role` to `account`.
           *
           * If `account` had not been already granted `role`, emits a {RoleGranted}
           * event.
           *
           * Requirements:
           *
           * - the caller must have ``role``'s admin role.
           */
          function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
              _grantRole(role, account);
          }
          /**
           * @dev Revokes `role` from `account`.
           *
           * If `account` had been granted `role`, emits a {RoleRevoked} event.
           *
           * Requirements:
           *
           * - the caller must have ``role``'s admin role.
           */
          function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
              _revokeRole(role, account);
          }
          /**
           * @dev Revokes `role` from the calling account.
           *
           * Roles are often managed via {grantRole} and {revokeRole}: this function's
           * purpose is to provide a mechanism for accounts to lose their privileges
           * if they are compromised (such as when a trusted device is misplaced).
           *
           * If the calling account had been revoked `role`, emits a {RoleRevoked}
           * event.
           *
           * Requirements:
           *
           * - the caller must be `account`.
           */
          function renounceRole(bytes32 role, address account) public virtual override {
              require(account == _msgSender(), "AccessControl: can only renounce roles for self");
              _revokeRole(role, account);
          }
          /**
           * @dev Grants `role` to `account`.
           *
           * If `account` had not been already granted `role`, emits a {RoleGranted}
           * event. Note that unlike {grantRole}, this function doesn't perform any
           * checks on the calling account.
           *
           * [WARNING]
           * ====
           * This function should only be called from the constructor when setting
           * up the initial roles for the system.
           *
           * Using this function in any other way is effectively circumventing the admin
           * system imposed by {AccessControl}.
           * ====
           *
           * NOTE: This function is deprecated in favor of {_grantRole}.
           */
          function _setupRole(bytes32 role, address account) internal virtual {
              _grantRole(role, account);
          }
          /**
           * @dev Sets `adminRole` as ``role``'s admin role.
           *
           * Emits a {RoleAdminChanged} event.
           */
          function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
              bytes32 previousAdminRole = getRoleAdmin(role);
              _roles[role].adminRole = adminRole;
              emit RoleAdminChanged(role, previousAdminRole, adminRole);
          }
          /**
           * @dev Grants `role` to `account`.
           *
           * Internal function without access restriction.
           */
          function _grantRole(bytes32 role, address account) internal virtual {
              if (!hasRole(role, account)) {
                  _roles[role].members[account] = true;
                  emit RoleGranted(role, account, _msgSender());
              }
          }
          /**
           * @dev Revokes `role` from `account`.
           *
           * Internal function without access restriction.
           */
          function _revokeRole(bytes32 role, address account) internal virtual {
              if (hasRole(role, account)) {
                  _roles[role].members[account] = false;
                  emit RoleRevoked(role, account, _msgSender());
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC1155/ERC1155.sol)
      pragma solidity ^0.8.0;
      import "./IERC1155.sol";
      import "./IERC1155Receiver.sol";
      import "./extensions/IERC1155MetadataURI.sol";
      import "../../utils/Address.sol";
      import "../../utils/Context.sol";
      import "../../utils/introspection/ERC165.sol";
      /**
       * @dev Implementation of the basic standard multi-token.
       * See https://eips.ethereum.org/EIPS/eip-1155
       * Originally based on code by Enjin: https://github.com/enjin/erc-1155
       *
       * _Available since v3.1._
       */
      contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
          using Address for address;
          // Mapping from token ID to account balances
          mapping(uint256 => mapping(address => uint256)) private _balances;
          // Mapping from account to operator approvals
          mapping(address => mapping(address => bool)) private _operatorApprovals;
          // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
          string private _uri;
          /**
           * @dev See {_setURI}.
           */
          constructor(string memory uri_) {
              _setURI(uri_);
          }
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
              return
                  interfaceId == type(IERC1155).interfaceId ||
                  interfaceId == type(IERC1155MetadataURI).interfaceId ||
                  super.supportsInterface(interfaceId);
          }
          /**
           * @dev See {IERC1155MetadataURI-uri}.
           *
           * This implementation returns the same URI for *all* token types. It relies
           * on the token type ID substitution mechanism
           * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
           *
           * Clients calling this function must replace the `\\{id\\}` substring with the
           * actual token type ID.
           */
          function uri(uint256) public view virtual override returns (string memory) {
              return _uri;
          }
          /**
           * @dev See {IERC1155-balanceOf}.
           *
           * Requirements:
           *
           * - `account` cannot be the zero address.
           */
          function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
              require(account != address(0), "ERC1155: balance query for the zero address");
              return _balances[id][account];
          }
          /**
           * @dev See {IERC1155-balanceOfBatch}.
           *
           * Requirements:
           *
           * - `accounts` and `ids` must have the same length.
           */
          function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
              public
              view
              virtual
              override
              returns (uint256[] memory)
          {
              require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");
              uint256[] memory batchBalances = new uint256[](accounts.length);
              for (uint256 i = 0; i < accounts.length; ++i) {
                  batchBalances[i] = balanceOf(accounts[i], ids[i]);
              }
              return batchBalances;
          }
          /**
           * @dev See {IERC1155-setApprovalForAll}.
           */
          function setApprovalForAll(address operator, bool approved) public virtual override {
              _setApprovalForAll(_msgSender(), operator, approved);
          }
          /**
           * @dev See {IERC1155-isApprovedForAll}.
           */
          function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
              return _operatorApprovals[account][operator];
          }
          /**
           * @dev See {IERC1155-safeTransferFrom}.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 id,
              uint256 amount,
              bytes memory data
          ) public virtual override {
              require(
                  from == _msgSender() || isApprovedForAll(from, _msgSender()),
                  "ERC1155: caller is not owner nor approved"
              );
              _safeTransferFrom(from, to, id, amount, data);
          }
          /**
           * @dev See {IERC1155-safeBatchTransferFrom}.
           */
          function safeBatchTransferFrom(
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) public virtual override {
              require(
                  from == _msgSender() || isApprovedForAll(from, _msgSender()),
                  "ERC1155: transfer caller is not owner nor approved"
              );
              _safeBatchTransferFrom(from, to, ids, amounts, data);
          }
          /**
           * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
           *
           * Emits a {TransferSingle} event.
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           * - `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 memory data
          ) internal virtual {
              require(to != address(0), "ERC1155: transfer to the zero address");
              address operator = _msgSender();
              uint256[] memory ids = _asSingletonArray(id);
              uint256[] memory amounts = _asSingletonArray(amount);
              _beforeTokenTransfer(operator, from, to, ids, amounts, data);
              uint256 fromBalance = _balances[id][from];
              require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
              unchecked {
                  _balances[id][from] = fromBalance - amount;
              }
              _balances[id][to] += amount;
              emit TransferSingle(operator, from, to, id, amount);
              _afterTokenTransfer(operator, from, to, ids, amounts, data);
              _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
          }
          /**
           * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
           *
           * Emits a {TransferBatch} event.
           *
           * Requirements:
           *
           * - 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[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual {
              require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
              require(to != address(0), "ERC1155: transfer to the zero address");
              address operator = _msgSender();
              _beforeTokenTransfer(operator, from, to, ids, amounts, data);
              for (uint256 i = 0; i < ids.length; ++i) {
                  uint256 id = ids[i];
                  uint256 amount = amounts[i];
                  uint256 fromBalance = _balances[id][from];
                  require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
                  unchecked {
                      _balances[id][from] = fromBalance - amount;
                  }
                  _balances[id][to] += amount;
              }
              emit TransferBatch(operator, from, to, ids, amounts);
              _afterTokenTransfer(operator, from, to, ids, amounts, data);
              _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
          }
          /**
           * @dev Sets a new URI for all token types, by relying on the token type ID
           * substitution mechanism
           * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
           *
           * By this mechanism, any occurrence of the `\\{id\\}` substring in either the
           * URI or any of the amounts in the JSON file at said URI will be replaced by
           * clients with the token type ID.
           *
           * For example, the `https://token-cdn-domain/\\{id\\}.json` URI would be
           * interpreted by clients as
           * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
           * for token type ID 0x4cce0.
           *
           * See {uri}.
           *
           * Because these URIs cannot be meaningfully represented by the {URI} event,
           * this function emits no events.
           */
          function _setURI(string memory newuri) internal virtual {
              _uri = newuri;
          }
          /**
           * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`.
           *
           * Emits a {TransferSingle} event.
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
           * acceptance magic value.
           */
          function _mint(
              address to,
              uint256 id,
              uint256 amount,
              bytes memory data
          ) internal virtual {
              require(to != address(0), "ERC1155: mint to the zero address");
              address operator = _msgSender();
              uint256[] memory ids = _asSingletonArray(id);
              uint256[] memory amounts = _asSingletonArray(amount);
              _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
              _balances[id][to] += amount;
              emit TransferSingle(operator, address(0), to, id, amount);
              _afterTokenTransfer(operator, address(0), to, ids, amounts, data);
              _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
          }
          /**
           * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
           *
           * 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 _mintBatch(
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual {
              require(to != address(0), "ERC1155: mint to the zero address");
              require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
              address operator = _msgSender();
              _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
              for (uint256 i = 0; i < ids.length; i++) {
                  _balances[ids[i]][to] += amounts[i];
              }
              emit TransferBatch(operator, address(0), to, ids, amounts);
              _afterTokenTransfer(operator, address(0), to, ids, amounts, data);
              _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
          }
          /**
           * @dev Destroys `amount` tokens of token type `id` from `from`
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `from` must have at least `amount` tokens of token type `id`.
           */
          function _burn(
              address from,
              uint256 id,
              uint256 amount
          ) internal virtual {
              require(from != address(0), "ERC1155: burn from the zero address");
              address operator = _msgSender();
              uint256[] memory ids = _asSingletonArray(id);
              uint256[] memory amounts = _asSingletonArray(amount);
              _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
              uint256 fromBalance = _balances[id][from];
              require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
              unchecked {
                  _balances[id][from] = fromBalance - amount;
              }
              emit TransferSingle(operator, from, address(0), id, amount);
              _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
          }
          /**
           * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
           *
           * Requirements:
           *
           * - `ids` and `amounts` must have the same length.
           */
          function _burnBatch(
              address from,
              uint256[] memory ids,
              uint256[] memory amounts
          ) internal virtual {
              require(from != address(0), "ERC1155: burn from the zero address");
              require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
              address operator = _msgSender();
              _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
              for (uint256 i = 0; i < ids.length; i++) {
                  uint256 id = ids[i];
                  uint256 amount = amounts[i];
                  uint256 fromBalance = _balances[id][from];
                  require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
                  unchecked {
                      _balances[id][from] = fromBalance - amount;
                  }
              }
              emit TransferBatch(operator, from, address(0), ids, amounts);
              _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
          }
          /**
           * @dev Approve `operator` to operate on all of `owner` tokens
           *
           * Emits a {ApprovalForAll} event.
           */
          function _setApprovalForAll(
              address owner,
              address operator,
              bool approved
          ) internal virtual {
              require(owner != operator, "ERC1155: setting approval status for self");
              _operatorApprovals[owner][operator] = approved;
              emit ApprovalForAll(owner, operator, approved);
          }
          /**
           * @dev Hook that is called before any token transfer. This includes minting
           * and burning, as well as batched variants.
           *
           * The same hook is called on both single and batched variants. For single
           * transfers, the length of the `id` and `amount` arrays will be 1.
           *
           * Calling conditions (for each `id` and `amount` pair):
           *
           * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
           * of token type `id` will be  transferred to `to`.
           * - When `from` is zero, `amount` tokens of token type `id` will be minted
           * for `to`.
           * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
           * will be burned.
           * - `from` and `to` are never both zero.
           * - `ids` and `amounts` have the same, non-zero length.
           *
           * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
           */
          function _beforeTokenTransfer(
              address operator,
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual {}
          /**
           * @dev Hook that is called after any token transfer. This includes minting
           * and burning, as well as batched variants.
           *
           * The same hook is called on both single and batched variants. For single
           * transfers, the length of the `id` and `amount` arrays will be 1.
           *
           * Calling conditions (for each `id` and `amount` pair):
           *
           * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
           * of token type `id` will be  transferred to `to`.
           * - When `from` is zero, `amount` tokens of token type `id` will be minted
           * for `to`.
           * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
           * will be burned.
           * - `from` and `to` are never both zero.
           * - `ids` and `amounts` have the same, non-zero length.
           *
           * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
           */
          function _afterTokenTransfer(
              address operator,
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual {}
          function _doSafeTransferAcceptanceCheck(
              address operator,
              address from,
              address to,
              uint256 id,
              uint256 amount,
              bytes memory data
          ) private {
              if (to.isContract()) {
                  try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                      if (response != IERC1155Receiver.onERC1155Received.selector) {
                          revert("ERC1155: ERC1155Receiver rejected tokens");
                      }
                  } catch Error(string memory reason) {
                      revert(reason);
                  } catch {
                      revert("ERC1155: transfer to non ERC1155Receiver implementer");
                  }
              }
          }
          function _doSafeBatchTransferAcceptanceCheck(
              address operator,
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) private {
              if (to.isContract()) {
                  try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                      bytes4 response
                  ) {
                      if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
                          revert("ERC1155: ERC1155Receiver rejected tokens");
                      }
                  } catch Error(string memory reason) {
                      revert(reason);
                  } catch {
                      revert("ERC1155: transfer to non ERC1155Receiver implementer");
                  }
              }
          }
          function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {
              uint256[] memory array = new uint256[](1);
              array[0] = element;
              return array;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/ERC1155Burnable.sol)
      pragma solidity ^0.8.0;
      import "../ERC1155.sol";
      /**
       * @dev Extension of {ERC1155} that allows token holders to destroy both their
       * own tokens and those that they have been approved to use.
       *
       * _Available since v3.1._
       */
      abstract contract ERC1155Burnable is ERC1155 {
          function burn(
              address account,
              uint256 id,
              uint256 value
          ) public virtual {
              require(
                  account == _msgSender() || isApprovedForAll(account, _msgSender()),
                  "ERC1155: caller is not owner nor approved"
              );
              _burn(account, id, value);
          }
          function burnBatch(
              address account,
              uint256[] memory ids,
              uint256[] memory values
          ) public virtual {
              require(
                  account == _msgSender() || isApprovedForAll(account, _msgSender()),
                  "ERC1155: caller is not owner nor approved"
              );
              _burnBatch(account, ids, values);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/ERC1155Pausable.sol)
      pragma solidity ^0.8.0;
      import "../ERC1155.sol";
      import "../../../security/Pausable.sol";
      /**
       * @dev ERC1155 token with pausable token transfers, minting and burning.
       *
       * Useful for scenarios such as preventing trades until the end of an evaluation
       * period, or having an emergency switch for freezing all token transfers in the
       * event of a large bug.
       *
       * _Available since v3.1._
       */
      abstract contract ERC1155Pausable is ERC1155, Pausable {
          /**
           * @dev See {ERC1155-_beforeTokenTransfer}.
           *
           * Requirements:
           *
           * - the contract must not be paused.
           */
          function _beforeTokenTransfer(
              address operator,
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual override {
              super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
              require(!paused(), "ERC1155Pausable: token transfer while paused");
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/ERC1155Supply.sol)
      pragma solidity ^0.8.0;
      import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol";
      /**
       * @dev Extension of ERC1155 that adds a max total supply per id.
       */
      abstract contract ERC1155CapSupply is ERC1155Supply {
          mapping(uint256 => uint256) private _maxSupply;
          mapping(uint256 => bool) private _frozenSupply;
          /**
           * @dev Set the max supply for a tokenId
           * remark: a max amount of 0 is equal to unlimited supply
           */
          function _setMaxSupply(
              uint256 id,
              uint256 max,
              bool freeze
          ) internal {
              require(!_frozenSupply[id], "ERC1155CapSupply: supply frozen");
              require(max == 0 || max >= totalSupply(id), "ERC1155CapSupply: invalid max supply");
              if (freeze) _frozenSupply[id] = true;
              _maxSupply[id] = max;
          }
          /**
           * @dev Max amount of tokens with a given id.
           * remark: a max amount of 0 is equal to unlimited supply
           */
          function maxSupply(uint256 id) public view virtual returns (uint256) {
              return _maxSupply[id];
          }
          /**
           * @dev See {ERC1155-_beforeTokenTransfer}.
           */
          function _beforeTokenTransfer(
              address operator,
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual override {
              if (from == address(0)) {
                  for (uint256 i = 0; i < ids.length; ++i) {
                      require(
                          _maxSupply[ids[i]] == 0 || totalSupply(ids[i]) + amounts[i] <= _maxSupply[ids[i]],
                          "ERC1155CapSupply: Above max supply"
                      );
                  }
              }
              super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
      /**
       * @dev Extension of ERC1155 to add a name
       */
      abstract contract ERC1155Metadata is ERC1155 {
          string private _name;
          constructor(string memory name_) {
              _name = name_;
          }
          /**
           * @dev the name of the token
           */
          function name() public view virtual returns (string memory) {
              return _name;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "@openzeppelin/contracts/access/AccessControl.sol";
      import "@openzeppelin/contracts/security/Pausable.sol";
      abstract contract PauserAccess is AccessControl, Pausable {
          bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
          modifier onlyPausers() {
              require(hasRole(PAUSER_ROLE, _msgSender()), "Access: pauser role required");
              _;
          }
          /**
           * @dev Pauses all token transfers.
           */
          function pause() public virtual onlyPausers {
              _pause();
          }
          /**
           * @dev Unpauses all token transfers.
           */
          function unpause() public virtual onlyPausers {
              _unpause();
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      interface IMintableERC1155 {
          function mint(
              address to,
              uint256 id,
              uint256 amount,
              bytes memory data
          ) external;
          function mintBatch(
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      interface IProxyRegistry {
          function proxies(address account) external view returns (address);
          function contracts(address caller) external view returns (bool);
      }
      interface IAuthenticatedProxy {
          function user() external view returns (address);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev External interface of AccessControl declared to support ERC165 detection.
       */
      interface IAccessControl {
          /**
           * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
           *
           * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
           * {RoleAdminChanged} not being emitted signaling this.
           *
           * _Available since v3.1._
           */
          event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
          /**
           * @dev Emitted when `account` is granted `role`.
           *
           * `sender` is the account that originated the contract call, an admin role
           * bearer except when using {AccessControl-_setupRole}.
           */
          event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
          /**
           * @dev Emitted when `account` is revoked `role`.
           *
           * `sender` is the account that originated the contract call:
           *   - if using `revokeRole`, it is the admin role bearer
           *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
           */
          event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
          /**
           * @dev Returns `true` if `account` has been granted `role`.
           */
          function hasRole(bytes32 role, address account) external view returns (bool);
          /**
           * @dev Returns the admin role that controls `role`. See {grantRole} and
           * {revokeRole}.
           *
           * To change a role's admin, use {AccessControl-_setRoleAdmin}.
           */
          function getRoleAdmin(bytes32 role) external view returns (bytes32);
          /**
           * @dev Grants `role` to `account`.
           *
           * If `account` had not been already granted `role`, emits a {RoleGranted}
           * event.
           *
           * Requirements:
           *
           * - the caller must have ``role``'s admin role.
           */
          function grantRole(bytes32 role, address account) external;
          /**
           * @dev Revokes `role` from `account`.
           *
           * If `account` had been granted `role`, emits a {RoleRevoked} event.
           *
           * Requirements:
           *
           * - the caller must have ``role``'s admin role.
           */
          function revokeRole(bytes32 role, address account) external;
          /**
           * @dev Revokes `role` from the calling account.
           *
           * Roles are often managed via {grantRole} and {revokeRole}: this function's
           * purpose is to provide a mechanism for accounts to lose their privileges
           * if they are compromised (such as when a trusted device is misplaced).
           *
           * If the calling account had been granted `role`, emits a {RoleRevoked}
           * event.
           *
           * Requirements:
           *
           * - the caller must be `account`.
           */
          function renounceRole(bytes32 role, address account) external;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev String operations.
       */
      library Strings {
          bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
          /**
           * @dev Converts a `uint256` to its ASCII `string` decimal representation.
           */
          function toString(uint256 value) internal pure returns (string memory) {
              // Inspired by OraclizeAPI's implementation - MIT licence
              // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
              if (value == 0) {
                  return "0";
              }
              uint256 temp = value;
              uint256 digits;
              while (temp != 0) {
                  digits++;
                  temp /= 10;
              }
              bytes memory buffer = new bytes(digits);
              while (value != 0) {
                  digits -= 1;
                  buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                  value /= 10;
              }
              return string(buffer);
          }
          /**
           * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
           */
          function toHexString(uint256 value) internal pure returns (string memory) {
              if (value == 0) {
                  return "0x00";
              }
              uint256 temp = value;
              uint256 length = 0;
              while (temp != 0) {
                  length++;
                  temp >>= 8;
              }
              return toHexString(value, length);
          }
          /**
           * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
           */
          function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
              bytes memory buffer = new bytes(2 * length + 2);
              buffer[0] = "0";
              buffer[1] = "x";
              for (uint256 i = 2 * length + 1; i > 1; --i) {
                  buffer[i] = _HEX_SYMBOLS[value & 0xf];
                  value >>= 4;
              }
              require(value == 0, "Strings: hex length insufficient");
              return string(buffer);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
      pragma solidity ^0.8.0;
      import "./IERC165.sol";
      /**
       * @dev Implementation of the {IERC165} interface.
       *
       * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
       * for the additional interface id that will be supported. For example:
       *
       * ```solidity
       * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
       *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
       * }
       * ```
       *
       * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
       */
      abstract contract ERC165 is IERC165 {
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
              return interfaceId == type(IERC165).interfaceId;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC165 standard, as defined in the
       * https://eips.ethereum.org/EIPS/eip-165[EIP].
       *
       * Implementers can declare support of contract interfaces, which can then be
       * queried by others ({ERC165Checker}).
       *
       * For an implementation, see {ERC165}.
       */
      interface IERC165 {
          /**
           * @dev Returns true if this contract implements the interface defined by
           * `interfaceId`. See the corresponding
           * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
           * to learn more about how these ids are created.
           *
           * This function call must use less than 30 000 gas.
           */
          function supportsInterface(bytes4 interfaceId) external view returns (bool);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (token/ERC1155/IERC1155.sol)
      pragma solidity ^0.8.0;
      import "../../utils/introspection/IERC165.sol";
      /**
       * @dev Required interface of an ERC1155 compliant contract, as defined in the
       * https://eips.ethereum.org/EIPS/eip-1155[EIP].
       *
       * _Available since v3.1._
       */
      interface IERC1155 is IERC165 {
          /**
           * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
           */
          event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
          /**
           * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
           * transfers.
           */
          event TransferBatch(
              address indexed operator,
              address indexed from,
              address indexed to,
              uint256[] ids,
              uint256[] values
          );
          /**
           * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
           * `approved`.
           */
          event ApprovalForAll(address indexed account, address indexed operator, bool approved);
          /**
           * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
           *
           * If an {URI} event was emitted for `id`, the standard
           * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
           * returned by {IERC1155MetadataURI-uri}.
           */
          event URI(string value, uint256 indexed id);
          /**
           * @dev Returns the amount of tokens of token type `id` owned by `account`.
           *
           * Requirements:
           *
           * - `account` cannot be the zero address.
           */
          function balanceOf(address account, uint256 id) external view returns (uint256);
          /**
           * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
           *
           * Requirements:
           *
           * - `accounts` and `ids` must have the same length.
           */
          function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
              external
              view
              returns (uint256[] memory);
          /**
           * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
           *
           * Emits an {ApprovalForAll} event.
           *
           * Requirements:
           *
           * - `operator` cannot be the caller.
           */
          function setApprovalForAll(address operator, bool approved) external;
          /**
           * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
           *
           * See {setApprovalForAll}.
           */
          function isApprovedForAll(address account, address operator) external view returns (bool);
          /**
           * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
           *
           * Emits a {TransferSingle} event.
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
           * - `from` must have a balance of tokens of type `id` of at least `amount`.
           * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
           * acceptance magic value.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 id,
              uint256 amount,
              bytes calldata data
          ) external;
          /**
           * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
           *
           * Emits a {TransferBatch} event.
           *
           * Requirements:
           *
           * - `ids` and `amounts` must have the same length.
           * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
           * acceptance magic value.
           */
          function safeBatchTransferFrom(
              address from,
              address to,
              uint256[] calldata ids,
              uint256[] calldata amounts,
              bytes calldata data
          ) external;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)
      pragma solidity ^0.8.0;
      import "../../utils/introspection/IERC165.sol";
      /**
       * @dev _Available since v3.1._
       */
      interface IERC1155Receiver is IERC165 {
          /**
           * @dev Handles the receipt of a single ERC1155 token type. This function is
           * called at the end of a `safeTransferFrom` after the balance has been updated.
           *
           * NOTE: 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.
           *
           * NOTE: To accept the transfer(s), this must return
           * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
           * (i.e. 0xbc197c81, or its own function selector).
           *
           * @param operator The address which initiated the batch transfer (i.e. msg.sender)
           * @param from The address which previously owned the token
           * @param ids An array containing ids of each token being transferred (order and length must match values array)
           * @param values An array containing amounts of each token being transferred (order and length must match ids array)
           * @param data Additional data with no specified format
           * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
           */
          function onERC1155BatchReceived(
              address operator,
              address from,
              uint256[] calldata ids,
              uint256[] calldata values,
              bytes calldata data
          ) external returns (bytes4);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol)
      pragma solidity ^0.8.0;
      import "../IERC1155.sol";
      /**
       * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
       * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
       *
       * _Available since v3.1._
       */
      interface IERC1155MetadataURI is IERC1155 {
          /**
           * @dev Returns the URI for token type `id`.
           *
           * If the `\\{id\\}` substring is present in the URI, it must be replaced by
           * clients with the actual token type ID.
           */
          function uri(uint256 id) external view returns (string memory);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)
      pragma solidity ^0.8.1;
      /**
       * @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
           * ====
           *
           * [IMPORTANT]
           * ====
           * You shouldn't rely on `isContract` to protect against flash loan attacks!
           *
           * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
           * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
           * constructor.
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies on extcodesize/address.code.length, which returns 0
              // for contracts in construction, since the code is only stored at the end
              // of the constructor execution.
              return account.code.length > 0;
          }
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
              (bool success, ) = recipient.call{value: amount}("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain `call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCall(target, data, "Address: low-level call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              require(isContract(target), "Address: call to non-contract");
              (bool success, bytes memory returndata) = target.call{value: value}(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal view returns (bytes memory) {
              require(isContract(target), "Address: static call to non-contract");
              (bool success, bytes memory returndata) = target.staticcall(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionDelegateCall(target, data, "Address: low-level delegate call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(isContract(target), "Address: delegate call to non-contract");
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
           * revert reason using the provided one.
           *
           * _Available since v4.3._
           */
          function verifyCallResult(
              bool success,
              bytes memory returndata,
              string memory errorMessage
          ) internal pure returns (bytes memory) {
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)
      pragma solidity ^0.8.0;
      import "../utils/Context.sol";
      /**
       * @dev Contract module which allows children to implement an emergency stop
       * mechanism that can be triggered by an authorized account.
       *
       * This module is used through inheritance. It will make available the
       * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
       * the functions of your contract. Note that they will not be pausable by
       * simply including this module, only once the modifiers are put in place.
       */
      abstract contract Pausable is Context {
          /**
           * @dev Emitted when the pause is triggered by `account`.
           */
          event Paused(address account);
          /**
           * @dev Emitted when the pause is lifted by `account`.
           */
          event Unpaused(address account);
          bool private _paused;
          /**
           * @dev Initializes the contract in unpaused state.
           */
          constructor() {
              _paused = false;
          }
          /**
           * @dev Returns true if the contract is paused, and false otherwise.
           */
          function paused() public view virtual returns (bool) {
              return _paused;
          }
          /**
           * @dev Modifier to make a function callable only when the contract is not paused.
           *
           * Requirements:
           *
           * - The contract must not be paused.
           */
          modifier whenNotPaused() {
              require(!paused(), "Pausable: paused");
              _;
          }
          /**
           * @dev Modifier to make a function callable only when the contract is paused.
           *
           * Requirements:
           *
           * - The contract must be paused.
           */
          modifier whenPaused() {
              require(paused(), "Pausable: not paused");
              _;
          }
          /**
           * @dev Triggers stopped state.
           *
           * Requirements:
           *
           * - The contract must not be paused.
           */
          function _pause() internal virtual whenNotPaused {
              _paused = true;
              emit Paused(_msgSender());
          }
          /**
           * @dev Returns to normal state.
           *
           * Requirements:
           *
           * - The contract must be paused.
           */
          function _unpause() internal virtual whenPaused {
              _paused = false;
              emit Unpaused(_msgSender());
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC1155/extensions/ERC1155Supply.sol)
      pragma solidity ^0.8.0;
      import "../ERC1155.sol";
      /**
       * @dev Extension of ERC1155 that adds tracking of total supply per id.
       *
       * Useful for scenarios where Fungible and Non-fungible tokens have to be
       * clearly identified. Note: While a totalSupply of 1 might mean the
       * corresponding is an NFT, there is no guarantees that no other token with the
       * same id are not going to be minted.
       */
      abstract contract ERC1155Supply is ERC1155 {
          mapping(uint256 => uint256) private _totalSupply;
          /**
           * @dev Total amount of tokens in with a given id.
           */
          function totalSupply(uint256 id) public view virtual returns (uint256) {
              return _totalSupply[id];
          }
          /**
           * @dev Indicates whether any token exist with a given id, or not.
           */
          function exists(uint256 id) public view virtual returns (bool) {
              return ERC1155Supply.totalSupply(id) > 0;
          }
          /**
           * @dev See {ERC1155-_beforeTokenTransfer}.
           */
          function _beforeTokenTransfer(
              address operator,
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual override {
              super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
              if (from == address(0)) {
                  for (uint256 i = 0; i < ids.length; ++i) {
                      _totalSupply[ids[i]] += amounts[i];
                  }
              }
              if (to == address(0)) {
                  for (uint256 i = 0; i < ids.length; ++i) {
                      uint256 id = ids[i];
                      uint256 amount = amounts[i];
                      uint256 supply = _totalSupply[id];
                      require(supply >= amount, "ERC1155: burn amount exceeds totalSupply");
                      unchecked {
                          _totalSupply[id] = supply - amount;
                      }
                  }
              }
          }
      }
      

      File 3 of 3: KompeteGameAssetFactory
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (access/AccessControl.sol)
      pragma solidity ^0.8.0;
      import "./IAccessControl.sol";
      import "../utils/Context.sol";
      import "../utils/Strings.sol";
      import "../utils/introspection/ERC165.sol";
      /**
       * @dev Contract module that allows children to implement role-based access
       * control mechanisms. This is a lightweight version that doesn't allow enumerating role
       * members except through off-chain means by accessing the contract event logs. Some
       * applications may benefit from on-chain enumerability, for those cases see
       * {AccessControlEnumerable}.
       *
       * Roles are referred to by their `bytes32` identifier. These should be exposed
       * in the external API and be unique. The best way to achieve this is by
       * using `public constant` hash digests:
       *
       * ```
       * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
       * ```
       *
       * Roles can be used to represent a set of permissions. To restrict access to a
       * function call, use {hasRole}:
       *
       * ```
       * function foo() public {
       *     require(hasRole(MY_ROLE, msg.sender));
       *     ...
       * }
       * ```
       *
       * Roles can be granted and revoked dynamically via the {grantRole} and
       * {revokeRole} functions. Each role has an associated admin role, and only
       * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
       *
       * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
       * that only accounts with this role will be able to grant or revoke other
       * roles. More complex role relationships can be created by using
       * {_setRoleAdmin}.
       *
       * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
       * grant and revoke this role. Extra precautions should be taken to secure
       * accounts that have been granted it.
       */
      abstract contract AccessControl is Context, IAccessControl, ERC165 {
          struct RoleData {
              mapping(address => bool) members;
              bytes32 adminRole;
          }
          mapping(bytes32 => RoleData) private _roles;
          bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
          /**
           * @dev Modifier that checks that an account has a specific role. Reverts
           * with a standardized message including the required role.
           *
           * The format of the revert reason is given by the following regular expression:
           *
           *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
           *
           * _Available since v4.1._
           */
          modifier onlyRole(bytes32 role) {
              _checkRole(role);
              _;
          }
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
              return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
          }
          /**
           * @dev Returns `true` if `account` has been granted `role`.
           */
          function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
              return _roles[role].members[account];
          }
          /**
           * @dev Revert with a standard message if `_msgSender()` is missing `role`.
           * Overriding this function changes the behavior of the {onlyRole} modifier.
           *
           * Format of the revert message is described in {_checkRole}.
           *
           * _Available since v4.6._
           */
          function _checkRole(bytes32 role) internal view virtual {
              _checkRole(role, _msgSender());
          }
          /**
           * @dev Revert with a standard message if `account` is missing `role`.
           *
           * The format of the revert reason is given by the following regular expression:
           *
           *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
           */
          function _checkRole(bytes32 role, address account) internal view virtual {
              if (!hasRole(role, account)) {
                  revert(
                      string(
                          abi.encodePacked(
                              "AccessControl: account ",
                              Strings.toHexString(uint160(account), 20),
                              " is missing role ",
                              Strings.toHexString(uint256(role), 32)
                          )
                      )
                  );
              }
          }
          /**
           * @dev Returns the admin role that controls `role`. See {grantRole} and
           * {revokeRole}.
           *
           * To change a role's admin, use {_setRoleAdmin}.
           */
          function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
              return _roles[role].adminRole;
          }
          /**
           * @dev Grants `role` to `account`.
           *
           * If `account` had not been already granted `role`, emits a {RoleGranted}
           * event.
           *
           * Requirements:
           *
           * - the caller must have ``role``'s admin role.
           */
          function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
              _grantRole(role, account);
          }
          /**
           * @dev Revokes `role` from `account`.
           *
           * If `account` had been granted `role`, emits a {RoleRevoked} event.
           *
           * Requirements:
           *
           * - the caller must have ``role``'s admin role.
           */
          function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
              _revokeRole(role, account);
          }
          /**
           * @dev Revokes `role` from the calling account.
           *
           * Roles are often managed via {grantRole} and {revokeRole}: this function's
           * purpose is to provide a mechanism for accounts to lose their privileges
           * if they are compromised (such as when a trusted device is misplaced).
           *
           * If the calling account had been revoked `role`, emits a {RoleRevoked}
           * event.
           *
           * Requirements:
           *
           * - the caller must be `account`.
           */
          function renounceRole(bytes32 role, address account) public virtual override {
              require(account == _msgSender(), "AccessControl: can only renounce roles for self");
              _revokeRole(role, account);
          }
          /**
           * @dev Grants `role` to `account`.
           *
           * If `account` had not been already granted `role`, emits a {RoleGranted}
           * event. Note that unlike {grantRole}, this function doesn't perform any
           * checks on the calling account.
           *
           * [WARNING]
           * ====
           * This function should only be called from the constructor when setting
           * up the initial roles for the system.
           *
           * Using this function in any other way is effectively circumventing the admin
           * system imposed by {AccessControl}.
           * ====
           *
           * NOTE: This function is deprecated in favor of {_grantRole}.
           */
          function _setupRole(bytes32 role, address account) internal virtual {
              _grantRole(role, account);
          }
          /**
           * @dev Sets `adminRole` as ``role``'s admin role.
           *
           * Emits a {RoleAdminChanged} event.
           */
          function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
              bytes32 previousAdminRole = getRoleAdmin(role);
              _roles[role].adminRole = adminRole;
              emit RoleAdminChanged(role, previousAdminRole, adminRole);
          }
          /**
           * @dev Grants `role` to `account`.
           *
           * Internal function without access restriction.
           */
          function _grantRole(bytes32 role, address account) internal virtual {
              if (!hasRole(role, account)) {
                  _roles[role].members[account] = true;
                  emit RoleGranted(role, account, _msgSender());
              }
          }
          /**
           * @dev Revokes `role` from `account`.
           *
           * Internal function without access restriction.
           */
          function _revokeRole(bytes32 role, address account) internal virtual {
              if (hasRole(role, account)) {
                  _roles[role].members[account] = false;
                  emit RoleRevoked(role, account, _msgSender());
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev External interface of AccessControl declared to support ERC165 detection.
       */
      interface IAccessControl {
          /**
           * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
           *
           * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
           * {RoleAdminChanged} not being emitted signaling this.
           *
           * _Available since v3.1._
           */
          event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
          /**
           * @dev Emitted when `account` is granted `role`.
           *
           * `sender` is the account that originated the contract call, an admin role
           * bearer except when using {AccessControl-_setupRole}.
           */
          event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
          /**
           * @dev Emitted when `account` is revoked `role`.
           *
           * `sender` is the account that originated the contract call:
           *   - if using `revokeRole`, it is the admin role bearer
           *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
           */
          event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
          /**
           * @dev Returns `true` if `account` has been granted `role`.
           */
          function hasRole(bytes32 role, address account) external view returns (bool);
          /**
           * @dev Returns the admin role that controls `role`. See {grantRole} and
           * {revokeRole}.
           *
           * To change a role's admin, use {AccessControl-_setRoleAdmin}.
           */
          function getRoleAdmin(bytes32 role) external view returns (bytes32);
          /**
           * @dev Grants `role` to `account`.
           *
           * If `account` had not been already granted `role`, emits a {RoleGranted}
           * event.
           *
           * Requirements:
           *
           * - the caller must have ``role``'s admin role.
           */
          function grantRole(bytes32 role, address account) external;
          /**
           * @dev Revokes `role` from `account`.
           *
           * If `account` had been granted `role`, emits a {RoleRevoked} event.
           *
           * Requirements:
           *
           * - the caller must have ``role``'s admin role.
           */
          function revokeRole(bytes32 role, address account) external;
          /**
           * @dev Revokes `role` from the calling account.
           *
           * Roles are often managed via {grantRole} and {revokeRole}: this function's
           * purpose is to provide a mechanism for accounts to lose their privileges
           * if they are compromised (such as when a trusted device is misplaced).
           *
           * If the calling account had been granted `role`, emits a {RoleRevoked}
           * event.
           *
           * Requirements:
           *
           * - the caller must be `account`.
           */
          function renounceRole(bytes32 role, address account) external;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)
      pragma solidity ^0.8.0;
      import "../utils/Context.sol";
      /**
       * @dev Contract module which allows children to implement an emergency stop
       * mechanism that can be triggered by an authorized account.
       *
       * This module is used through inheritance. It will make available the
       * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
       * the functions of your contract. Note that they will not be pausable by
       * simply including this module, only once the modifiers are put in place.
       */
      abstract contract Pausable is Context {
          /**
           * @dev Emitted when the pause is triggered by `account`.
           */
          event Paused(address account);
          /**
           * @dev Emitted when the pause is lifted by `account`.
           */
          event Unpaused(address account);
          bool private _paused;
          /**
           * @dev Initializes the contract in unpaused state.
           */
          constructor() {
              _paused = false;
          }
          /**
           * @dev Returns true if the contract is paused, and false otherwise.
           */
          function paused() public view virtual returns (bool) {
              return _paused;
          }
          /**
           * @dev Modifier to make a function callable only when the contract is not paused.
           *
           * Requirements:
           *
           * - The contract must not be paused.
           */
          modifier whenNotPaused() {
              require(!paused(), "Pausable: paused");
              _;
          }
          /**
           * @dev Modifier to make a function callable only when the contract is paused.
           *
           * Requirements:
           *
           * - The contract must be paused.
           */
          modifier whenPaused() {
              require(paused(), "Pausable: not paused");
              _;
          }
          /**
           * @dev Triggers stopped state.
           *
           * Requirements:
           *
           * - The contract must not be paused.
           */
          function _pause() internal virtual whenNotPaused {
              _paused = true;
              emit Paused(_msgSender());
          }
          /**
           * @dev Returns to normal state.
           *
           * Requirements:
           *
           * - The contract must be paused.
           */
          function _unpause() internal virtual whenPaused {
              _paused = false;
              emit Unpaused(_msgSender());
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC1155/ERC1155.sol)
      pragma solidity ^0.8.0;
      import "./IERC1155.sol";
      import "./IERC1155Receiver.sol";
      import "./extensions/IERC1155MetadataURI.sol";
      import "../../utils/Address.sol";
      import "../../utils/Context.sol";
      import "../../utils/introspection/ERC165.sol";
      /**
       * @dev Implementation of the basic standard multi-token.
       * See https://eips.ethereum.org/EIPS/eip-1155
       * Originally based on code by Enjin: https://github.com/enjin/erc-1155
       *
       * _Available since v3.1._
       */
      contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
          using Address for address;
          // Mapping from token ID to account balances
          mapping(uint256 => mapping(address => uint256)) private _balances;
          // Mapping from account to operator approvals
          mapping(address => mapping(address => bool)) private _operatorApprovals;
          // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
          string private _uri;
          /**
           * @dev See {_setURI}.
           */
          constructor(string memory uri_) {
              _setURI(uri_);
          }
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
              return
                  interfaceId == type(IERC1155).interfaceId ||
                  interfaceId == type(IERC1155MetadataURI).interfaceId ||
                  super.supportsInterface(interfaceId);
          }
          /**
           * @dev See {IERC1155MetadataURI-uri}.
           *
           * This implementation returns the same URI for *all* token types. It relies
           * on the token type ID substitution mechanism
           * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
           *
           * Clients calling this function must replace the `\\{id\\}` substring with the
           * actual token type ID.
           */
          function uri(uint256) public view virtual override returns (string memory) {
              return _uri;
          }
          /**
           * @dev See {IERC1155-balanceOf}.
           *
           * Requirements:
           *
           * - `account` cannot be the zero address.
           */
          function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
              require(account != address(0), "ERC1155: balance query for the zero address");
              return _balances[id][account];
          }
          /**
           * @dev See {IERC1155-balanceOfBatch}.
           *
           * Requirements:
           *
           * - `accounts` and `ids` must have the same length.
           */
          function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
              public
              view
              virtual
              override
              returns (uint256[] memory)
          {
              require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");
              uint256[] memory batchBalances = new uint256[](accounts.length);
              for (uint256 i = 0; i < accounts.length; ++i) {
                  batchBalances[i] = balanceOf(accounts[i], ids[i]);
              }
              return batchBalances;
          }
          /**
           * @dev See {IERC1155-setApprovalForAll}.
           */
          function setApprovalForAll(address operator, bool approved) public virtual override {
              _setApprovalForAll(_msgSender(), operator, approved);
          }
          /**
           * @dev See {IERC1155-isApprovedForAll}.
           */
          function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
              return _operatorApprovals[account][operator];
          }
          /**
           * @dev See {IERC1155-safeTransferFrom}.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 id,
              uint256 amount,
              bytes memory data
          ) public virtual override {
              require(
                  from == _msgSender() || isApprovedForAll(from, _msgSender()),
                  "ERC1155: caller is not owner nor approved"
              );
              _safeTransferFrom(from, to, id, amount, data);
          }
          /**
           * @dev See {IERC1155-safeBatchTransferFrom}.
           */
          function safeBatchTransferFrom(
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) public virtual override {
              require(
                  from == _msgSender() || isApprovedForAll(from, _msgSender()),
                  "ERC1155: transfer caller is not owner nor approved"
              );
              _safeBatchTransferFrom(from, to, ids, amounts, data);
          }
          /**
           * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
           *
           * Emits a {TransferSingle} event.
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           * - `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 memory data
          ) internal virtual {
              require(to != address(0), "ERC1155: transfer to the zero address");
              address operator = _msgSender();
              uint256[] memory ids = _asSingletonArray(id);
              uint256[] memory amounts = _asSingletonArray(amount);
              _beforeTokenTransfer(operator, from, to, ids, amounts, data);
              uint256 fromBalance = _balances[id][from];
              require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
              unchecked {
                  _balances[id][from] = fromBalance - amount;
              }
              _balances[id][to] += amount;
              emit TransferSingle(operator, from, to, id, amount);
              _afterTokenTransfer(operator, from, to, ids, amounts, data);
              _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
          }
          /**
           * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
           *
           * Emits a {TransferBatch} event.
           *
           * Requirements:
           *
           * - 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[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual {
              require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
              require(to != address(0), "ERC1155: transfer to the zero address");
              address operator = _msgSender();
              _beforeTokenTransfer(operator, from, to, ids, amounts, data);
              for (uint256 i = 0; i < ids.length; ++i) {
                  uint256 id = ids[i];
                  uint256 amount = amounts[i];
                  uint256 fromBalance = _balances[id][from];
                  require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
                  unchecked {
                      _balances[id][from] = fromBalance - amount;
                  }
                  _balances[id][to] += amount;
              }
              emit TransferBatch(operator, from, to, ids, amounts);
              _afterTokenTransfer(operator, from, to, ids, amounts, data);
              _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
          }
          /**
           * @dev Sets a new URI for all token types, by relying on the token type ID
           * substitution mechanism
           * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
           *
           * By this mechanism, any occurrence of the `\\{id\\}` substring in either the
           * URI or any of the amounts in the JSON file at said URI will be replaced by
           * clients with the token type ID.
           *
           * For example, the `https://token-cdn-domain/\\{id\\}.json` URI would be
           * interpreted by clients as
           * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
           * for token type ID 0x4cce0.
           *
           * See {uri}.
           *
           * Because these URIs cannot be meaningfully represented by the {URI} event,
           * this function emits no events.
           */
          function _setURI(string memory newuri) internal virtual {
              _uri = newuri;
          }
          /**
           * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`.
           *
           * Emits a {TransferSingle} event.
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
           * acceptance magic value.
           */
          function _mint(
              address to,
              uint256 id,
              uint256 amount,
              bytes memory data
          ) internal virtual {
              require(to != address(0), "ERC1155: mint to the zero address");
              address operator = _msgSender();
              uint256[] memory ids = _asSingletonArray(id);
              uint256[] memory amounts = _asSingletonArray(amount);
              _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
              _balances[id][to] += amount;
              emit TransferSingle(operator, address(0), to, id, amount);
              _afterTokenTransfer(operator, address(0), to, ids, amounts, data);
              _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
          }
          /**
           * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
           *
           * 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 _mintBatch(
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual {
              require(to != address(0), "ERC1155: mint to the zero address");
              require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
              address operator = _msgSender();
              _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
              for (uint256 i = 0; i < ids.length; i++) {
                  _balances[ids[i]][to] += amounts[i];
              }
              emit TransferBatch(operator, address(0), to, ids, amounts);
              _afterTokenTransfer(operator, address(0), to, ids, amounts, data);
              _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
          }
          /**
           * @dev Destroys `amount` tokens of token type `id` from `from`
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `from` must have at least `amount` tokens of token type `id`.
           */
          function _burn(
              address from,
              uint256 id,
              uint256 amount
          ) internal virtual {
              require(from != address(0), "ERC1155: burn from the zero address");
              address operator = _msgSender();
              uint256[] memory ids = _asSingletonArray(id);
              uint256[] memory amounts = _asSingletonArray(amount);
              _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
              uint256 fromBalance = _balances[id][from];
              require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
              unchecked {
                  _balances[id][from] = fromBalance - amount;
              }
              emit TransferSingle(operator, from, address(0), id, amount);
              _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
          }
          /**
           * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
           *
           * Requirements:
           *
           * - `ids` and `amounts` must have the same length.
           */
          function _burnBatch(
              address from,
              uint256[] memory ids,
              uint256[] memory amounts
          ) internal virtual {
              require(from != address(0), "ERC1155: burn from the zero address");
              require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
              address operator = _msgSender();
              _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
              for (uint256 i = 0; i < ids.length; i++) {
                  uint256 id = ids[i];
                  uint256 amount = amounts[i];
                  uint256 fromBalance = _balances[id][from];
                  require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
                  unchecked {
                      _balances[id][from] = fromBalance - amount;
                  }
              }
              emit TransferBatch(operator, from, address(0), ids, amounts);
              _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
          }
          /**
           * @dev Approve `operator` to operate on all of `owner` tokens
           *
           * Emits a {ApprovalForAll} event.
           */
          function _setApprovalForAll(
              address owner,
              address operator,
              bool approved
          ) internal virtual {
              require(owner != operator, "ERC1155: setting approval status for self");
              _operatorApprovals[owner][operator] = approved;
              emit ApprovalForAll(owner, operator, approved);
          }
          /**
           * @dev Hook that is called before any token transfer. This includes minting
           * and burning, as well as batched variants.
           *
           * The same hook is called on both single and batched variants. For single
           * transfers, the length of the `id` and `amount` arrays will be 1.
           *
           * Calling conditions (for each `id` and `amount` pair):
           *
           * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
           * of token type `id` will be  transferred to `to`.
           * - When `from` is zero, `amount` tokens of token type `id` will be minted
           * for `to`.
           * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
           * will be burned.
           * - `from` and `to` are never both zero.
           * - `ids` and `amounts` have the same, non-zero length.
           *
           * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
           */
          function _beforeTokenTransfer(
              address operator,
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual {}
          /**
           * @dev Hook that is called after any token transfer. This includes minting
           * and burning, as well as batched variants.
           *
           * The same hook is called on both single and batched variants. For single
           * transfers, the length of the `id` and `amount` arrays will be 1.
           *
           * Calling conditions (for each `id` and `amount` pair):
           *
           * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
           * of token type `id` will be  transferred to `to`.
           * - When `from` is zero, `amount` tokens of token type `id` will be minted
           * for `to`.
           * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
           * will be burned.
           * - `from` and `to` are never both zero.
           * - `ids` and `amounts` have the same, non-zero length.
           *
           * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
           */
          function _afterTokenTransfer(
              address operator,
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual {}
          function _doSafeTransferAcceptanceCheck(
              address operator,
              address from,
              address to,
              uint256 id,
              uint256 amount,
              bytes memory data
          ) private {
              if (to.isContract()) {
                  try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                      if (response != IERC1155Receiver.onERC1155Received.selector) {
                          revert("ERC1155: ERC1155Receiver rejected tokens");
                      }
                  } catch Error(string memory reason) {
                      revert(reason);
                  } catch {
                      revert("ERC1155: transfer to non ERC1155Receiver implementer");
                  }
              }
          }
          function _doSafeBatchTransferAcceptanceCheck(
              address operator,
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) private {
              if (to.isContract()) {
                  try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                      bytes4 response
                  ) {
                      if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
                          revert("ERC1155: ERC1155Receiver rejected tokens");
                      }
                  } catch Error(string memory reason) {
                      revert(reason);
                  } catch {
                      revert("ERC1155: transfer to non ERC1155Receiver implementer");
                  }
              }
          }
          function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {
              uint256[] memory array = new uint256[](1);
              array[0] = element;
              return array;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/ERC1155Burnable.sol)
      pragma solidity ^0.8.0;
      import "../ERC1155.sol";
      /**
       * @dev Extension of {ERC1155} that allows token holders to destroy both their
       * own tokens and those that they have been approved to use.
       *
       * _Available since v3.1._
       */
      abstract contract ERC1155Burnable is ERC1155 {
          function burn(
              address account,
              uint256 id,
              uint256 value
          ) public virtual {
              require(
                  account == _msgSender() || isApprovedForAll(account, _msgSender()),
                  "ERC1155: caller is not owner nor approved"
              );
              _burn(account, id, value);
          }
          function burnBatch(
              address account,
              uint256[] memory ids,
              uint256[] memory values
          ) public virtual {
              require(
                  account == _msgSender() || isApprovedForAll(account, _msgSender()),
                  "ERC1155: caller is not owner nor approved"
              );
              _burnBatch(account, ids, values);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/ERC1155Pausable.sol)
      pragma solidity ^0.8.0;
      import "../ERC1155.sol";
      import "../../../security/Pausable.sol";
      /**
       * @dev ERC1155 token with pausable token transfers, minting and burning.
       *
       * Useful for scenarios such as preventing trades until the end of an evaluation
       * period, or having an emergency switch for freezing all token transfers in the
       * event of a large bug.
       *
       * _Available since v3.1._
       */
      abstract contract ERC1155Pausable is ERC1155, Pausable {
          /**
           * @dev See {ERC1155-_beforeTokenTransfer}.
           *
           * Requirements:
           *
           * - the contract must not be paused.
           */
          function _beforeTokenTransfer(
              address operator,
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual override {
              super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
              require(!paused(), "ERC1155Pausable: token transfer while paused");
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC1155/extensions/ERC1155Supply.sol)
      pragma solidity ^0.8.0;
      import "../ERC1155.sol";
      /**
       * @dev Extension of ERC1155 that adds tracking of total supply per id.
       *
       * Useful for scenarios where Fungible and Non-fungible tokens have to be
       * clearly identified. Note: While a totalSupply of 1 might mean the
       * corresponding is an NFT, there is no guarantees that no other token with the
       * same id are not going to be minted.
       */
      abstract contract ERC1155Supply is ERC1155 {
          mapping(uint256 => uint256) private _totalSupply;
          /**
           * @dev Total amount of tokens in with a given id.
           */
          function totalSupply(uint256 id) public view virtual returns (uint256) {
              return _totalSupply[id];
          }
          /**
           * @dev Indicates whether any token exist with a given id, or not.
           */
          function exists(uint256 id) public view virtual returns (bool) {
              return ERC1155Supply.totalSupply(id) > 0;
          }
          /**
           * @dev See {ERC1155-_beforeTokenTransfer}.
           */
          function _beforeTokenTransfer(
              address operator,
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual override {
              super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
              if (from == address(0)) {
                  for (uint256 i = 0; i < ids.length; ++i) {
                      _totalSupply[ids[i]] += amounts[i];
                  }
              }
              if (to == address(0)) {
                  for (uint256 i = 0; i < ids.length; ++i) {
                      uint256 id = ids[i];
                      uint256 amount = amounts[i];
                      uint256 supply = _totalSupply[id];
                      require(supply >= amount, "ERC1155: burn amount exceeds totalSupply");
                      unchecked {
                          _totalSupply[id] = supply - amount;
                      }
                  }
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol)
      pragma solidity ^0.8.0;
      import "../IERC1155.sol";
      /**
       * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
       * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
       *
       * _Available since v3.1._
       */
      interface IERC1155MetadataURI is IERC1155 {
          /**
           * @dev Returns the URI for token type `id`.
           *
           * If the `\\{id\\}` substring is present in the URI, it must be replaced by
           * clients with the actual token type ID.
           */
          function uri(uint256 id) external view returns (string memory);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (token/ERC1155/IERC1155.sol)
      pragma solidity ^0.8.0;
      import "../../utils/introspection/IERC165.sol";
      /**
       * @dev Required interface of an ERC1155 compliant contract, as defined in the
       * https://eips.ethereum.org/EIPS/eip-1155[EIP].
       *
       * _Available since v3.1._
       */
      interface IERC1155 is IERC165 {
          /**
           * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
           */
          event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
          /**
           * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
           * transfers.
           */
          event TransferBatch(
              address indexed operator,
              address indexed from,
              address indexed to,
              uint256[] ids,
              uint256[] values
          );
          /**
           * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
           * `approved`.
           */
          event ApprovalForAll(address indexed account, address indexed operator, bool approved);
          /**
           * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
           *
           * If an {URI} event was emitted for `id`, the standard
           * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
           * returned by {IERC1155MetadataURI-uri}.
           */
          event URI(string value, uint256 indexed id);
          /**
           * @dev Returns the amount of tokens of token type `id` owned by `account`.
           *
           * Requirements:
           *
           * - `account` cannot be the zero address.
           */
          function balanceOf(address account, uint256 id) external view returns (uint256);
          /**
           * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
           *
           * Requirements:
           *
           * - `accounts` and `ids` must have the same length.
           */
          function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
              external
              view
              returns (uint256[] memory);
          /**
           * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
           *
           * Emits an {ApprovalForAll} event.
           *
           * Requirements:
           *
           * - `operator` cannot be the caller.
           */
          function setApprovalForAll(address operator, bool approved) external;
          /**
           * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
           *
           * See {setApprovalForAll}.
           */
          function isApprovedForAll(address account, address operator) external view returns (bool);
          /**
           * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
           *
           * Emits a {TransferSingle} event.
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
           * - `from` must have a balance of tokens of type `id` of at least `amount`.
           * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
           * acceptance magic value.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 id,
              uint256 amount,
              bytes calldata data
          ) external;
          /**
           * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
           *
           * Emits a {TransferBatch} event.
           *
           * Requirements:
           *
           * - `ids` and `amounts` must have the same length.
           * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
           * acceptance magic value.
           */
          function safeBatchTransferFrom(
              address from,
              address to,
              uint256[] calldata ids,
              uint256[] calldata amounts,
              bytes calldata data
          ) external;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)
      pragma solidity ^0.8.0;
      import "../../utils/introspection/IERC165.sol";
      /**
       * @dev _Available since v3.1._
       */
      interface IERC1155Receiver is IERC165 {
          /**
           * @dev Handles the receipt of a single ERC1155 token type. This function is
           * called at the end of a `safeTransferFrom` after the balance has been updated.
           *
           * NOTE: 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.
           *
           * NOTE: To accept the transfer(s), this must return
           * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
           * (i.e. 0xbc197c81, or its own function selector).
           *
           * @param operator The address which initiated the batch transfer (i.e. msg.sender)
           * @param from The address which previously owned the token
           * @param ids An array containing ids of each token being transferred (order and length must match values array)
           * @param values An array containing amounts of each token being transferred (order and length must match ids array)
           * @param data Additional data with no specified format
           * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
           */
          function onERC1155BatchReceived(
              address operator,
              address from,
              uint256[] calldata ids,
              uint256[] calldata values,
              bytes calldata data
          ) external returns (bytes4);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP.
       */
      interface IERC20 {
          /**
           * @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);
          /**
           * @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 `to`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address to, 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 `from` to `to` 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 from,
              address to,
              uint256 amount
          ) external returns (bool);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721.sol)
      pragma solidity ^0.8.0;
      import "../../utils/introspection/IERC165.sol";
      /**
       * @dev Required interface of an ERC721 compliant contract.
       */
      interface IERC721 is IERC165 {
          /**
           * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
           */
          event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
          /**
           * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
           */
          event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
          /**
           * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
           */
          event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
          /**
           * @dev Returns the number of tokens in ``owner``'s account.
           */
          function balanceOf(address owner) external view returns (uint256 balance);
          /**
           * @dev Returns the owner of the `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function ownerOf(uint256 tokenId) external view returns (address owner);
          /**
           * @dev Safely transfers `tokenId` token from `from` to `to`.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must exist and be owned by `from`.
           * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
           * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
           *
           * Emits a {Transfer} event.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 tokenId,
              bytes calldata data
          ) external;
          /**
           * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
           * are aware of the ERC721 protocol to prevent tokens from being forever locked.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must exist and be owned by `from`.
           * - If the caller is not `from`, it must 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 Approve or remove `operator` as an operator for the caller.
           * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
           *
           * Requirements:
           *
           * - The `operator` cannot be the caller.
           *
           * Emits an {ApprovalForAll} event.
           */
          function setApprovalForAll(address operator, bool _approved) external;
          /**
           * @dev Returns the account approved for `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function getApproved(uint256 tokenId) external view returns (address operator);
          /**
           * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
           *
           * See {setApprovalForAll}
           */
          function isApprovedForAll(address owner, address operator) external view returns (bool);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)
      pragma solidity ^0.8.1;
      /**
       * @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
           * ====
           *
           * [IMPORTANT]
           * ====
           * You shouldn't rely on `isContract` to protect against flash loan attacks!
           *
           * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
           * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
           * constructor.
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies on extcodesize/address.code.length, which returns 0
              // for contracts in construction, since the code is only stored at the end
              // of the constructor execution.
              return account.code.length > 0;
          }
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
              (bool success, ) = recipient.call{value: amount}("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain `call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCall(target, data, "Address: low-level call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              require(isContract(target), "Address: call to non-contract");
              (bool success, bytes memory returndata) = target.call{value: value}(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal view returns (bytes memory) {
              require(isContract(target), "Address: static call to non-contract");
              (bool success, bytes memory returndata) = target.staticcall(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionDelegateCall(target, data, "Address: low-level delegate call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(isContract(target), "Address: delegate call to non-contract");
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
           * revert reason using the provided one.
           *
           * _Available since v4.3._
           */
          function verifyCallResult(
              bool success,
              bytes memory returndata,
              string memory errorMessage
          ) internal pure returns (bytes memory) {
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/Context.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 Context {
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
      pragma solidity ^0.8.0;
      import "./IERC165.sol";
      /**
       * @dev Implementation of the {IERC165} interface.
       *
       * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
       * for the additional interface id that will be supported. For example:
       *
       * ```solidity
       * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
       *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
       * }
       * ```
       *
       * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
       */
      abstract contract ERC165 is IERC165 {
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
              return interfaceId == type(IERC165).interfaceId;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC165 standard, as defined in the
       * https://eips.ethereum.org/EIPS/eip-165[EIP].
       *
       * Implementers can declare support of contract interfaces, which can then be
       * queried by others ({ERC165Checker}).
       *
       * For an implementation, see {ERC165}.
       */
      interface IERC165 {
          /**
           * @dev Returns true if this contract implements the interface defined by
           * `interfaceId`. See the corresponding
           * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
           * to learn more about how these ids are created.
           *
           * This function call must use less than 30 000 gas.
           */
          function supportsInterface(bytes4 interfaceId) external view returns (bool);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev String operations.
       */
      library Strings {
          bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
          /**
           * @dev Converts a `uint256` to its ASCII `string` decimal representation.
           */
          function toString(uint256 value) internal pure returns (string memory) {
              // Inspired by OraclizeAPI's implementation - MIT licence
              // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
              if (value == 0) {
                  return "0";
              }
              uint256 temp = value;
              uint256 digits;
              while (temp != 0) {
                  digits++;
                  temp /= 10;
              }
              bytes memory buffer = new bytes(digits);
              while (value != 0) {
                  digits -= 1;
                  buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                  value /= 10;
              }
              return string(buffer);
          }
          /**
           * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
           */
          function toHexString(uint256 value) internal pure returns (string memory) {
              if (value == 0) {
                  return "0x00";
              }
              uint256 temp = value;
              uint256 length = 0;
              while (temp != 0) {
                  length++;
                  temp >>= 8;
              }
              return toHexString(value, length);
          }
          /**
           * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
           */
          function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
              bytes memory buffer = new bytes(2 * length + 2);
              buffer[0] = "0";
              buffer[1] = "x";
              for (uint256 i = 2 * length + 1; i > 1; --i) {
                  buffer[i] = _HEX_SYMBOLS[value & 0xf];
                  value >>= 4;
              }
              require(value == 0, "Strings: hex length insufficient");
              return string(buffer);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "./IMintableERC1155.sol";
      interface IKompeteGameAsset is IMintableERC1155 {
          function registry() external view returns (address);
          function setMaxSupply(
              uint256 id,
              uint256 max,
              bool freeze
          ) external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      interface IMintableERC1155 {
          function mint(
              address to,
              uint256 id,
              uint256 amount,
              bytes memory data
          ) external;
          function mintBatch(
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      interface IProxyRegistry {
          function proxies(address account) external view returns (address);
          function contracts(address caller) external view returns (bool);
      }
      interface IAuthenticatedProxy {
          function user() external view returns (address);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "@openzeppelin/contracts/utils/Context.sol";
      import "@openzeppelin/contracts/access/AccessControl.sol";
      import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
      import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol";
      import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Burnable.sol";
      import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Pausable.sol";
      import "./libraries/AccessControlRecoverable.sol";
      import "./libraries/OperatorAccess.sol";
      import "./interfaces/IKompeteGameAsset.sol";
      import "./interfaces/IProxyRegistry.sol";
      contract KompeteGameAssetFactory is Context, AccessControl, OperatorAccess, AccessControlRecoverable {
          IKompeteGameAsset public immutable assetCollection;
          constructor(IKompeteGameAsset _assetCollection) {
              assetCollection = _assetCollection;
              _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
          }
          modifier onlyOperatorsOrProxy() {
              if (!hasRole(OPERATOR_ROLE, _msgSender())) {
                  address registry = assetCollection.registry();
                  require(registry != address(0), "Factory: operator role or registry required");
                  IProxyRegistry proxyRegistry = IProxyRegistry(registry);
                  IAuthenticatedProxy proxy = IAuthenticatedProxy(_msgSender());
                  require(exists(address(proxy)), "Factory: invalid proxy");
                  address proxied = proxy.user();
                  require(proxied != address(0), "Factory: invalid proxied address");
                  address registered = address(proxyRegistry.proxies(proxied));
                  require(_msgSender() == registered, "Factory: invalid proxy for user");
                  require(hasRole(OPERATOR_ROLE, proxied), "Factory: operator role required for proxy");
              }
              _;
          }
          /**
           * @dev Mint tokens from the asset collection
           */
          function mint(
              address to,
              uint256 id,
              uint256 amount,
              bytes memory data
          ) public virtual onlyOperatorsOrProxy {
              assetCollection.mint(to, id, amount, data);
          }
          /**
           * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] variant of {mint}.
           */
          function mintBatch(
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) public virtual onlyOperatorsOrProxy {
              assetCollection.mintBatch(to, ids, amounts, data);
          }
          /**
           * @dev Set the max supply for a tokenId
           */
          function setMaxSupply(
              uint256 id,
              uint256 max,
              bool freeze
          ) external onlyOperators {
              assetCollection.setMaxSupply(id, max, freeze);
          }
          function exists(address what) internal view returns (bool) {
              uint256 size;
              assembly {
                  size := extcodesize(what)
              }
              return size > 0;
          }
          function canMint(address collection, address account) public view returns (bool) {
              return collection == address(assetCollection) && hasRole(OPERATOR_ROLE, account);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "@openzeppelin/contracts/access/AccessControl.sol";
      import "./Recoverable.sol";
      contract AccessControlRecoverable is AccessControl, Recoverable {
          bytes32 public constant RECOVER_ROLE = keccak256("RECOVER_ROLE");
          modifier onlyRecover() {
              require(hasRole(RECOVER_ROLE, _msgSender()), "Recoverable: recover role required");
              _;
          }
          function recoverERC721Token(
              address _to,
              address _token,
              uint256 _tokenId
          ) external onlyRecover {
              _recoverERC721Token(_to, _token, _tokenId);
          }
          function recoverERC1155Token(
              address _to,
              address _token,
              uint256 _tokenId,
              uint256 _amount
          ) external onlyRecover {
              _recoverERC1155Token(_to, _token, _tokenId, _amount);
          }
          function recoverERC20Token(
              address _to,
              address _token,
              uint256 _amount
          ) external onlyRecover {
              _recoverERC20Token(_to, _token, _amount);
          }
          function recoverEth(address payable _to, uint256 _amount) external onlyRecover {
              _recoverEth(_to, _amount);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "@openzeppelin/contracts/access/AccessControl.sol";
      abstract contract OperatorAccess is AccessControl {
          bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
          // Modifier for operator roles
          modifier onlyOperators() {
              require(hasRole(OPERATOR_ROLE, _msgSender()), "Access: operator role required");
              _;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      pragma abicoder v2;
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
      import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
      abstract contract Recoverable {
          event ERC721TokenRecovered(address indexed to, address indexed token, uint256 tokenId);
          event ERC1155TokenRecovered(address indexed to, address indexed token, uint256 tokenId, uint256 amount);
          event ERC20TokenRecovered(address indexed to, address indexed token, uint256 amount);
          event EthRecovered(address indexed to, uint256 amount);
          /**
           * @dev Allows to recover ERC721 tokens sent to the contract by mistake
           */
          function _recoverERC721Token(
              address _to,
              address _token,
              uint256 _tokenId
          ) internal {
              IERC721(_token).transferFrom(address(this), _to, _tokenId);
              emit ERC721TokenRecovered(_to, _token, _tokenId);
          }
          /**
           * @dev Allows to recover ERC1155 tokens sent to the contract by mistake
           */
          function _recoverERC1155Token(
              address _to,
              address _token,
              uint256 _tokenId,
              uint256 _amount
          ) internal {
              IERC1155(_token).safeTransferFrom(address(this), _to, _tokenId, _amount, "");
              emit ERC1155TokenRecovered(_to, _token, _tokenId, _amount);
          }
          /**
           * @dev Allows to recover ERC20 tokens sent to the contract by mistake
           */
          function _recoverERC20Token(
              address _to,
              address _token,
              uint256 _amount
          ) internal {
              IERC20(_token).transferFrom(address(this), _to, _amount);
              emit ERC20TokenRecovered(_to, _token, _amount);
          }
          /**
           * @dev Allows to recover ETH sent to the contract by mistake
           */
          function _recoverEth(address payable _to, uint256 _amount) internal {
              _to.transfer(_amount);
              emit EthRecovered(_to, _amount);
          }
      }