ETH Price: $2,331.62 (-1.25%)

Transaction Decoder

Block:
13463434 at Oct-21-2021 09:33:13 PM +UTC
Transaction Fee:
0.013620439735818318 ETH $31.76
Gas Used:
214,461 Gas / 63.510100838 Gwei

Emitted Events:

218 PersonalityPodERC721.Transfer( _from=0x00000000...000000000, _to=[Sender] 0xd2cb629a5c6e52bededece198c6077f4a83c6294, _tokenId=1497 )
219 OpenSeaFactoryImpl.Minted( _by=0xcb784060da27afa6bcbcdd0c1a3f03e52a9bd05c, _optionId=1497, _to=[Sender] 0xd2cb629a5c6e52bededece198c6077f4a83c6294 )
220 WyvernExchange.OrdersMatched( buyHash=0000000000000000000000000000000000000000000000000000000000000000, sellHash=B4DAD37046265A138CA2DC95983FAE88745F47C33B6FDE90F8B5FEF020A0AC76, maker=0x0738f702d1a7364d356729cb8845701885c487a1, taker=[Sender] 0xd2cb629a5c6e52bededece198c6077f4a83c6294, price=100000000000000000, metadata=0000000000000000000000000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x0738F702...885C487A1
(Alethea AI: Deployer)
696.176626461419059781 Eth696.274126461419059781 Eth0.0975
(Nanopool)
4,012.130150552956633964 Eth4,012.130472244456633964 Eth0.0003216915
0x5b325696...807C01073
(OpenSea: Wallet)
1,940.843709137056016915 Eth1,940.846209137056016915 Eth0.0025
0x7Be8076f...6C946D12b
0x86758d1c...86eebe768
0xD2CB629a...4A83c6294
4.693603113269307035 Eth
Nonce: 434
4.579982673533488717 Eth
Nonce: 435
0.113620439735818318
0xDd70AF84...134b5651C

Execution Trace

ETH 0.1 WyvernExchange.atomicMatch_( addrs=[0x7Be8076f4EA4A4AD08075C2508e481d6C946D12b, 0xD2CB629a5c6E52BededeCE198c6077f4A83c6294, 0x0738F702D1a7364d356729Cb8845701885C487A1, 0x0000000000000000000000000000000000000000, 0x86758d1c5aB95FF324F7E0b2cDEc38c86eebe768, 0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000, 0x7Be8076f4EA4A4AD08075C2508e481d6C946D12b, 0x0738F702D1a7364d356729Cb8845701885C487A1, 0x0000000000000000000000000000000000000000, 0x5b3256965e7C3cF26E11FCAf296DfC8807C01073, 0x86758d1c5aB95FF324F7E0b2cDEc38c86eebe768, 0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000], uints=[250, 0, 0, 0, 100000000000000000, 0, 1634851874, 0, 26548594646254699905628804642332509641184212409945739994473566541878366253138, 250, 0, 0, 0, 100000000000000000, 0, 1634850000, 1637528400, 37075025778200426667286764547007427480713686139877387974636884362699663030768], feeMethodsSidesKindsHowToCalls=[1, 0, 0, 0, 1, 1, 0, 0], calldataBuy=0x23B872DD0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000D2CB629A5C6E52BEDEDECE198C6077F4A83C62940000000000000000000000000000000000000000000000000000000000000000, calldataSell=0x23B872DD0000000000000000000000000738F702D1A7364D356729CB8845701885C487A100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, replacementPatternBuy=0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, replacementPatternSell=0x000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000000000000000000000000000, staticExtradataBuy=0x, staticExtradataSell=0x, vs=[28, 28], rssMetadata=[KNRrpwS74udywO6uHAQEUaOTX2CllD5w3s2koyMIRV0=, AmvyILtfmqs2ZuNKD8stco62RS5z/jo2nf47BYKF0Xg=, KNRrpwS74udywO6uHAQEUaOTX2CllD5w3s2koyMIRV0=, AmvyILtfmqs2ZuNKD8stco62RS5z/jo2nf47BYKF0Xg=, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=] )
  • Null: 0x000...001.b4dad370( )
  • WyvernProxyRegistry.proxies( 0x0738F702D1a7364d356729Cb8845701885C487A1 ) => ( 0xCb784060dA27aFA6bcbcdD0C1a3F03e52a9BD05C )
  • WyvernProxyRegistry.CALL( )
  • 0xcb784060da27afa6bcbcdd0c1a3f03e52a9bd05c.CALL( )
  • ETH 0.0025 OpenSea: Wallet.CALL( )
  • ETH 0.0975 Alethea AI: Deployer.CALL( )
  • 0xcb784060da27afa6bcbcdd0c1a3f03e52a9bd05c.1b0f7ba9( )
    • AuthenticatedProxy.proxy( dest=0x86758d1c5aB95FF324F7E0b2cDEc38c86eebe768, howToCall=0, calldata=0x23B872DD0000000000000000000000000738F702D1A7364D356729CB8845701885C487A1000000000000000000000000D2CB629A5C6E52BEDEDECE198C6077F4A83C62940000000000000000000000000000000000000000000000000000000000000000 ) => ( result=True )
      • WyvernProxyRegistry.contracts( 0x7Be8076f4EA4A4AD08075C2508e481d6C946D12b ) => ( True )
      • OpenSeaFactoryImpl.transferFrom( _from=0x0738F702D1a7364d356729Cb8845701885C487A1, _to=0xD2CB629a5c6E52BededeCE198c6077f4A83c6294, _tokenId=0 )
        • WyvernProxyRegistry.proxies( 0x0738F702D1a7364d356729Cb8845701885C487A1 ) => ( 0xCb784060dA27aFA6bcbcdD0C1a3F03e52a9BD05C )
        • PersonalityPodERC721.mint( _to=0xD2CB629a5c6E52BededeCE198c6077f4A83c6294, _tokenId=1497 )
          File 1 of 5: WyvernExchange
          pragma solidity ^0.4.13;
          
          library SafeMath {
          
            /**
            * @dev Multiplies two numbers, throws on overflow.
            */
            function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
              if (a == 0) {
                return 0;
              }
              c = a * b;
              assert(c / a == b);
              return c;
            }
          
            /**
            * @dev Integer division of two numbers, truncating the quotient.
            */
            function div(uint256 a, uint256 b) internal pure returns (uint256) {
              // assert(b > 0); // Solidity automatically throws when dividing by 0
              // uint256 c = a / b;
              // assert(a == b * c + a % b); // There is no case in which this doesn't hold
              return a / b;
            }
          
            /**
            * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
            */
            function sub(uint256 a, uint256 b) internal pure returns (uint256) {
              assert(b <= a);
              return a - b;
            }
          
            /**
            * @dev Adds two numbers, throws on overflow.
            */
            function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
              c = a + b;
              assert(c >= a);
              return c;
            }
          }
          
          contract Ownable {
            address public owner;
          
          
            event OwnershipRenounced(address indexed previousOwner);
            event OwnershipTransferred(
              address indexed previousOwner,
              address indexed newOwner
            );
          
          
            /**
             * @dev The Ownable constructor sets the original `owner` of the contract to the sender
             * account.
             */
            constructor() public {
              owner = msg.sender;
            }
          
            /**
             * @dev Throws if called by any account other than the owner.
             */
            modifier onlyOwner() {
              require(msg.sender == owner);
              _;
            }
          
            /**
             * @dev Allows the current owner to transfer control of the contract to a newOwner.
             * @param newOwner The address to transfer ownership to.
             */
            function transferOwnership(address newOwner) public onlyOwner {
              require(newOwner != address(0));
              emit OwnershipTransferred(owner, newOwner);
              owner = newOwner;
            }
          
            /**
             * @dev Allows the current owner to relinquish control of the contract.
             */
            function renounceOwnership() public onlyOwner {
              emit OwnershipRenounced(owner);
              owner = address(0);
            }
          }
          
          contract ERC20Basic {
            function totalSupply() public view returns (uint256);
            function balanceOf(address who) public view returns (uint256);
            function transfer(address to, uint256 value) public returns (bool);
            event Transfer(address indexed from, address indexed to, uint256 value);
          }
          
          contract ERC20 is ERC20Basic {
            function allowance(address owner, address spender)
              public view returns (uint256);
          
            function transferFrom(address from, address to, uint256 value)
              public returns (bool);
          
            function approve(address spender, uint256 value) public returns (bool);
            event Approval(
              address indexed owner,
              address indexed spender,
              uint256 value
            );
          }
          
          library ArrayUtils {
          
              /**
               * Replace bytes in an array with bytes in another array, guarded by a bitmask
               * Efficiency of this function is a bit unpredictable because of the EVM's word-specific model (arrays under 32 bytes will be slower)
               * 
               * @dev Mask must be the size of the byte array. A nonzero byte means the byte array can be changed.
               * @param array The original array
               * @param desired The target array
               * @param mask The mask specifying which bits can be changed
               * @return The updated byte array (the parameter will be modified inplace)
               */
              function guardedArrayReplace(bytes memory array, bytes memory desired, bytes memory mask)
                  internal
                  pure
              {
                  require(array.length == desired.length);
                  require(array.length == mask.length);
          
                  uint words = array.length / 0x20;
                  uint index = words * 0x20;
                  assert(index / 0x20 == words);
                  uint i;
          
                  for (i = 0; i < words; i++) {
                      /* Conceptually: array[i] = (!mask[i] && array[i]) || (mask[i] && desired[i]), bitwise in word chunks. */
                      assembly {
                          let commonIndex := mul(0x20, add(1, i))
                          let maskValue := mload(add(mask, commonIndex))
                          mstore(add(array, commonIndex), or(and(not(maskValue), mload(add(array, commonIndex))), and(maskValue, mload(add(desired, commonIndex)))))
                      }
                  }
          
                  /* Deal with the last section of the byte array. */
                  if (words > 0) {
                      /* This overlaps with bytes already set but is still more efficient than iterating through each of the remaining bytes individually. */
                      i = words;
                      assembly {
                          let commonIndex := mul(0x20, add(1, i))
                          let maskValue := mload(add(mask, commonIndex))
                          mstore(add(array, commonIndex), or(and(not(maskValue), mload(add(array, commonIndex))), and(maskValue, mload(add(desired, commonIndex)))))
                      }
                  } else {
                      /* If the byte array is shorter than a word, we must unfortunately do the whole thing bytewise.
                         (bounds checks could still probably be optimized away in assembly, but this is a rare case) */
                      for (i = index; i < array.length; i++) {
                          array[i] = ((mask[i] ^ 0xff) & array[i]) | (mask[i] & desired[i]);
                      }
                  }
              }
          
              /**
               * Test if two arrays are equal
               * Source: https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol
               * 
               * @dev Arrays must be of equal length, otherwise will return false
               * @param a First array
               * @param b Second array
               * @return Whether or not all bytes in the arrays are equal
               */
              function arrayEq(bytes memory a, bytes memory b)
                  internal
                  pure
                  returns (bool)
              {
                  bool success = true;
          
                  assembly {
                      let length := mload(a)
          
                      // if lengths don't match the arrays are not equal
                      switch eq(length, mload(b))
                      case 1 {
                          // cb is a circuit breaker in the for loop since there's
                          //  no said feature for inline assembly loops
                          // cb = 1 - don't breaker
                          // cb = 0 - break
                          let cb := 1
          
                          let mc := add(a, 0x20)
                          let end := add(mc, length)
          
                          for {
                              let cc := add(b, 0x20)
                          // the next line is the loop condition:
                          // while(uint(mc < end) + cb == 2)
                          } eq(add(lt(mc, end), cb), 2) {
                              mc := add(mc, 0x20)
                              cc := add(cc, 0x20)
                          } {
                              // if any of these checks fails then arrays are not equal
                              if iszero(eq(mload(mc), mload(cc))) {
                                  // unsuccess:
                                  success := 0
                                  cb := 0
                              }
                          }
                      }
                      default {
                          // unsuccess:
                          success := 0
                      }
                  }
          
                  return success;
              }
          
              /**
               * Unsafe write byte array into a memory location
               *
               * @param index Memory location
               * @param source Byte array to write
               * @return End memory index
               */
              function unsafeWriteBytes(uint index, bytes source)
                  internal
                  pure
                  returns (uint)
              {
                  if (source.length > 0) {
                      assembly {
                          let length := mload(source)
                          let end := add(source, add(0x20, length))
                          let arrIndex := add(source, 0x20)
                          let tempIndex := index
                          for { } eq(lt(arrIndex, end), 1) {
                              arrIndex := add(arrIndex, 0x20)
                              tempIndex := add(tempIndex, 0x20)
                          } {
                              mstore(tempIndex, mload(arrIndex))
                          }
                          index := add(index, length)
                      }
                  }
                  return index;
              }
          
              /**
               * Unsafe write address into a memory location
               *
               * @param index Memory location
               * @param source Address to write
               * @return End memory index
               */
              function unsafeWriteAddress(uint index, address source)
                  internal
                  pure
                  returns (uint)
              {
                  uint conv = uint(source) << 0x60;
                  assembly {
                      mstore(index, conv)
                      index := add(index, 0x14)
                  }
                  return index;
              }
          
              /**
               * Unsafe write uint into a memory location
               *
               * @param index Memory location
               * @param source uint to write
               * @return End memory index
               */
              function unsafeWriteUint(uint index, uint source)
                  internal
                  pure
                  returns (uint)
              {
                  assembly {
                      mstore(index, source)
                      index := add(index, 0x20)
                  }
                  return index;
              }
          
              /**
               * Unsafe write uint8 into a memory location
               *
               * @param index Memory location
               * @param source uint8 to write
               * @return End memory index
               */
              function unsafeWriteUint8(uint index, uint8 source)
                  internal
                  pure
                  returns (uint)
              {
                  assembly {
                      mstore8(index, source)
                      index := add(index, 0x1)
                  }
                  return index;
              }
          
          }
          
          contract ReentrancyGuarded {
          
              bool reentrancyLock = false;
          
              /* Prevent a contract function from being reentrant-called. */
              modifier reentrancyGuard {
                  if (reentrancyLock) {
                      revert();
                  }
                  reentrancyLock = true;
                  _;
                  reentrancyLock = false;
              }
          
          }
          
          contract TokenRecipient {
              event ReceivedEther(address indexed sender, uint amount);
              event ReceivedTokens(address indexed from, uint256 value, address indexed token, bytes extraData);
          
              /**
               * @dev Receive tokens and generate a log event
               * @param from Address from which to transfer tokens
               * @param value Amount of tokens to transfer
               * @param token Address of token
               * @param extraData Additional data to log
               */
              function receiveApproval(address from, uint256 value, address token, bytes extraData) public {
                  ERC20 t = ERC20(token);
                  require(t.transferFrom(from, this, value));
                  emit ReceivedTokens(from, value, token, extraData);
              }
          
              /**
               * @dev Receive Ether and generate a log event
               */
              function () payable public {
                  emit ReceivedEther(msg.sender, msg.value);
              }
          }
          
          contract ExchangeCore is ReentrancyGuarded, Ownable {
          
              /* The token used to pay exchange fees. */
              ERC20 public exchangeToken;
          
              /* User registry. */
              ProxyRegistry public registry;
          
              /* Token transfer proxy. */
              TokenTransferProxy public tokenTransferProxy;
          
              /* 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). */
              mapping(bytes32 => bool) public approvedOrders;
          
              /* For split fee orders, minimum required protocol maker fee, in basis points. Paid to owner (who can change it). */
              uint public minimumMakerProtocolFee = 0;
          
              /* For split fee orders, minimum required protocol taker fee, in basis points. Paid to owner (who can change it). */
              uint public minimumTakerProtocolFee = 0;
          
              /* Recipient of protocol fees. */
              address public protocolFeeRecipient;
          
              /* Fee method: protocol fee or split fee. */
              enum FeeMethod { ProtocolFee, SplitFee }
          
              /* Inverse basis point. */
              uint public constant INVERSE_BASIS_POINT = 10000;
          
              /* An ECDSA signature. */ 
              struct Sig {
                  /* v parameter */
                  uint8 v;
                  /* r parameter */
                  bytes32 r;
                  /* s parameter */
                  bytes32 s;
              }
          
              /* An order on the exchange. */
              struct Order {
                  /* Exchange address, intended as a versioning mechanism. */
                  address exchange;
                  /* Order maker address. */
                  address maker;
                  /* Order taker address, if specified. */
                  address taker;
                  /* Maker relayer fee of the order, unused for taker order. */
                  uint makerRelayerFee;
                  /* Taker relayer fee of the order, or maximum taker fee for a taker order. */
                  uint takerRelayerFee;
                  /* Maker protocol fee of the order, unused for taker order. */
                  uint makerProtocolFee;
                  /* Taker protocol fee of the order, or maximum taker fee for a taker order. */
                  uint takerProtocolFee;
                  /* Order fee recipient or zero address for taker order. */
                  address feeRecipient;
                  /* Fee method (protocol token or split fee). */
                  FeeMethod feeMethod;
                  /* Side (buy/sell). */
                  SaleKindInterface.Side side;
                  /* Kind of sale. */
                  SaleKindInterface.SaleKind saleKind;
                  /* Target. */
                  address target;
                  /* HowToCall. */
                  AuthenticatedProxy.HowToCall howToCall;
                  /* Calldata. */
                  bytes calldata;
                  /* Calldata replacement pattern, or an empty byte array for no replacement. */
                  bytes replacementPattern;
                  /* Static call target, zero-address for no static call. */
                  address staticTarget;
                  /* Static call extra data. */
                  bytes staticExtradata;
                  /* 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). */
                  uint basePrice;
                  /* Auction extra parameter - minimum bid increment for English auctions, starting/ending price difference. */
                  uint extra;
                  /* Listing timestamp. */
                  uint listingTime;
                  /* Expiration timestamp - 0 for no expiry. */
                  uint expirationTime;
                  /* Order salt, used to prevent duplicate hashes. */
                  uint salt;
              }
              
              event OrderApprovedPartOne    (bytes32 indexed hash, address exchange, address indexed maker, address taker, uint makerRelayerFee, uint takerRelayerFee, uint makerProtocolFee, uint takerProtocolFee, address indexed feeRecipient, FeeMethod feeMethod, SaleKindInterface.Side side, SaleKindInterface.SaleKind saleKind, address target);
              event OrderApprovedPartTwo    (bytes32 indexed hash, AuthenticatedProxy.HowToCall howToCall, bytes calldata, bytes replacementPattern, address staticTarget, bytes staticExtradata, address paymentToken, uint basePrice, uint extra, uint listingTime, uint expirationTime, uint salt, bool orderbookInclusionDesired);
              event OrderCancelled          (bytes32 indexed hash);
              event OrdersMatched           (bytes32 buyHash, bytes32 sellHash, address indexed maker, address indexed taker, uint price, bytes32 indexed metadata);
          
              /**
               * @dev Change the minimum maker fee paid to the protocol (owner only)
               * @param newMinimumMakerProtocolFee New fee to set in basis points
               */
              function changeMinimumMakerProtocolFee(uint newMinimumMakerProtocolFee)
                  public
                  onlyOwner
              {
                  minimumMakerProtocolFee = newMinimumMakerProtocolFee;
              }
          
              /**
               * @dev Change the minimum taker fee paid to the protocol (owner only)
               * @param newMinimumTakerProtocolFee New fee to set in basis points
               */
              function changeMinimumTakerProtocolFee(uint newMinimumTakerProtocolFee)
                  public
                  onlyOwner
              {
                  minimumTakerProtocolFee = newMinimumTakerProtocolFee;
              }
          
              /**
               * @dev Change the protocol fee recipient (owner only)
               * @param newProtocolFeeRecipient New protocol fee recipient address
               */
              function changeProtocolFeeRecipient(address newProtocolFeeRecipient)
                  public
                  onlyOwner
              {
                  protocolFeeRecipient = newProtocolFeeRecipient;
              }
          
              /**
               * @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, uint amount)
                  internal
              {
                  if (amount > 0) {
                      require(tokenTransferProxy.transferFrom(token, from, to, amount));
                  }
              }
          
              /**
               * @dev Charge a fee in protocol tokens
               * @param from Address to charge fees
               * @param to Address to receive fees
               * @param amount Amount of protocol tokens to charge
               */
              function chargeProtocolFee(address from, address to, uint amount)
                  internal
              {
                  transferTokens(exchangeToken, from, to, amount);
              }
          
              /**
               * @dev Execute a STATICCALL (introduced with Ethereum Metropolis, non-state-modifying external call)
               * @param target Contract to call
               * @param calldata Calldata (appended to extradata)
               * @param extradata Base data for STATICCALL (probably function selector and argument encoding)
               * @return The result of the call (success or failure)
               */
              function staticCall(address target, bytes memory calldata, bytes memory extradata)
                  public
                  view
                  returns (bool result)
              {
                  bytes memory combined = new bytes(calldata.length + extradata.length);
                  uint index;
                  assembly {
                      index := add(combined, 0x20)
                  }
                  index = ArrayUtils.unsafeWriteBytes(index, extradata);
                  ArrayUtils.unsafeWriteBytes(index, calldata);
                  assembly {
                      result := staticcall(gas, target, add(combined, 0x20), mload(combined), mload(0x40), 0)
                  }
                  return result;
              }
          
              /**
               * Calculate size of an order struct when tightly packed
               *
               * @param order Order to calculate size of
               * @return Size in bytes
               */
              function sizeOf(Order memory order)
                  internal
                  pure
                  returns (uint)
              {
                  return ((0x14 * 7) + (0x20 * 9) + 4 + order.calldata.length + order.replacementPattern.length + order.staticExtradata.length);
              }
          
              /**
               * @dev Hash an order, returning the canonical order hash, without the message prefix
               * @param order Order to hash
               * @return Hash of order
               */
              function hashOrder(Order memory order)
                  internal
                  pure
                  returns (bytes32 hash)
              {
                  /* Unfortunately abi.encodePacked doesn't work here, stack size constraints. */
                  uint size = sizeOf(order);
                  bytes memory array = new bytes(size);
                  uint index;
                  assembly {
                      index := add(array, 0x20)
                  }
                  index = ArrayUtils.unsafeWriteAddress(index, order.exchange);
                  index = ArrayUtils.unsafeWriteAddress(index, order.maker);
                  index = ArrayUtils.unsafeWriteAddress(index, order.taker);
                  index = ArrayUtils.unsafeWriteUint(index, order.makerRelayerFee);
                  index = ArrayUtils.unsafeWriteUint(index, order.takerRelayerFee);
                  index = ArrayUtils.unsafeWriteUint(index, order.makerProtocolFee);
                  index = ArrayUtils.unsafeWriteUint(index, order.takerProtocolFee);
                  index = ArrayUtils.unsafeWriteAddress(index, order.feeRecipient);
                  index = ArrayUtils.unsafeWriteUint8(index, uint8(order.feeMethod));
                  index = ArrayUtils.unsafeWriteUint8(index, uint8(order.side));
                  index = ArrayUtils.unsafeWriteUint8(index, uint8(order.saleKind));
                  index = ArrayUtils.unsafeWriteAddress(index, order.target);
                  index = ArrayUtils.unsafeWriteUint8(index, uint8(order.howToCall));
                  index = ArrayUtils.unsafeWriteBytes(index, order.calldata);
                  index = ArrayUtils.unsafeWriteBytes(index, order.replacementPattern);
                  index = ArrayUtils.unsafeWriteAddress(index, order.staticTarget);
                  index = ArrayUtils.unsafeWriteBytes(index, order.staticExtradata);
                  index = ArrayUtils.unsafeWriteAddress(index, order.paymentToken);
                  index = ArrayUtils.unsafeWriteUint(index, order.basePrice);
                  index = ArrayUtils.unsafeWriteUint(index, order.extra);
                  index = ArrayUtils.unsafeWriteUint(index, order.listingTime);
                  index = ArrayUtils.unsafeWriteUint(index, order.expirationTime);
                  index = ArrayUtils.unsafeWriteUint(index, order.salt);
                  assembly {
                      hash := keccak256(add(array, 0x20), size)
                  }
                  return hash;
              }
          
              /**
               * @dev Hash an order, returning the hash that a client must sign, including the standard message prefix
               * @param order Order to hash
               * @return Hash of message prefix and order hash per Ethereum format
               */
              function hashToSign(Order memory order)
                  internal
                  pure
                  returns (bytes32)
              {
                  return keccak256("\x19Ethereum Signed Message:\n32", hashOrder(order));
              }
          
              /**
               * @dev Assert an order is valid and return its hash
               * @param order Order to validate
               * @param sig ECDSA signature
               */
              function requireValidOrder(Order memory order, Sig memory sig)
                  internal
                  view
                  returns (bytes32)
              {
                  bytes32 hash = hashToSign(order);
                  require(validateOrder(hash, order, sig));
                  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 be targeted at this protocol version (this Exchange contract). */
                  if (order.exchange != address(this)) {
                      return false;
                  }
          
                  /* Order must possess valid sale kind parameter combination. */
                  if (!SaleKindInterface.validateParameters(order.saleKind, order.expirationTime)) {
                      return false;
                  }
          
                  /* If using the split fee method, order must have sufficient protocol fees. */
                  if (order.feeMethod == FeeMethod.SplitFee && (order.makerProtocolFee < minimumMakerProtocolFee || order.takerProtocolFee < minimumTakerProtocolFee)) {
                      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 sig ECDSA signature
               */
              function validateOrder(bytes32 hash, Order memory order, Sig memory sig) 
                  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;
                  }
                  
                  /* Order authentication. Order must be either:
                  /* (a) previously approved */
                  if (approvedOrders[hash]) {
                      return true;
                  }
          
                  /* or (b) ECDSA-signed by maker. */
                  if (ecrecover(hash, sig.v, sig.r, sig.s) == order.maker) {
                      return true;
                  }
          
                  return false;
              }
          
              /**
               * @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. */
                  require(msg.sender == order.maker);
          
                  /* Calculate order hash. */
                  bytes32 hash = hashToSign(order);
          
                  /* Assert order has not already been approved. */
                  require(!approvedOrders[hash]);
          
                  /* EFFECTS */
              
                  /* Mark order as approved. */
                  approvedOrders[hash] = true;
            
                  /* Log approval event. Must be split in two due to Solidity stack size limitations. */
                  {
                      emit OrderApprovedPartOne(hash, order.exchange, order.maker, order.taker, order.makerRelayerFee, order.takerRelayerFee, order.makerProtocolFee, order.takerProtocolFee, order.feeRecipient, order.feeMethod, order.side, order.saleKind, order.target);
                  }
                  {   
                      emit OrderApprovedPartTwo(hash, order.howToCall, order.calldata, order.replacementPattern, order.staticTarget, order.staticExtradata, order.paymentToken, order.basePrice, order.extra, order.listingTime, order.expirationTime, order.salt, 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 sig ECDSA signature
               */
              function cancelOrder(Order memory order, Sig memory sig) 
                  internal
              {
                  /* CHECKS */
          
                  /* Calculate order hash. */
                  bytes32 hash = requireValidOrder(order, sig);
          
                  /* Assert sender is authorized to cancel order. */
                  require(msg.sender == order.maker);
            
                  /* EFFECTS */
                
                  /* Mark order as cancelled, preventing it from being matched. */
                  cancelledOrFinalized[hash] = true;
          
                  /* Log cancel event. */
                  emit OrderCancelled(hash);
              }
          
              /**
               * @dev Calculate the current price of an order (convenience function)
               * @param order Order to calculate the price of
               * @return The current price of the order
               */
              function calculateCurrentPrice (Order memory order)
                  internal  
                  view
                  returns (uint)
              {
                  return SaleKindInterface.calculateFinalPrice(order.side, order.saleKind, order.basePrice, order.extra, order.listingTime, order.expirationTime);
              }
          
              /**
               * @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)
                  view
                  internal
                  returns (uint)
              {
                  /* Calculate sell price. */
                  uint sellPrice = SaleKindInterface.calculateFinalPrice(sell.side, sell.saleKind, sell.basePrice, sell.extra, sell.listingTime, sell.expirationTime);
          
                  /* Calculate buy price. */
                  uint buyPrice = SaleKindInterface.calculateFinalPrice(buy.side, buy.saleKind, buy.basePrice, buy.extra, buy.listingTime, buy.expirationTime);
          
                  /* 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 (uint)
              {
                  /* Only payable in the special case of unwrapped Ether. */
                  if (sell.paymentToken != address(0)) {
                      require(msg.value == 0);
                  }
          
                  /* Calculate match price. */
                  uint price = calculateMatchPrice(buy, sell);
          
                  /* 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, sell.maker, price);
                  }
          
                  /* Amount that will be received by seller (for Ether). */
                  uint receiveAmount = price;
          
                  /* Amount that must be sent by buyer (for Ether). */
                  uint requiredAmount = price;
          
                  /* 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.takerRelayerFee <= buy.takerRelayerFee);
          
                      if (sell.feeMethod == FeeMethod.SplitFee) {
                          /* 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.makerRelayerFee > 0) {
                              uint makerRelayerFee = SafeMath.div(SafeMath.mul(sell.makerRelayerFee, price), INVERSE_BASIS_POINT);
                              if (sell.paymentToken == address(0)) {
                                  receiveAmount = SafeMath.sub(receiveAmount, makerRelayerFee);
                                  sell.feeRecipient.transfer(makerRelayerFee);
                              } else {
                                  transferTokens(sell.paymentToken, sell.maker, sell.feeRecipient, makerRelayerFee);
                              }
                          }
          
                          if (sell.takerRelayerFee > 0) {
                              uint takerRelayerFee = SafeMath.div(SafeMath.mul(sell.takerRelayerFee, price), INVERSE_BASIS_POINT);
                              if (sell.paymentToken == address(0)) {
                                  requiredAmount = SafeMath.add(requiredAmount, takerRelayerFee);
                                  sell.feeRecipient.transfer(takerRelayerFee);
                              } else {
                                  transferTokens(sell.paymentToken, buy.maker, sell.feeRecipient, takerRelayerFee);
                              }
                          }
          
                          if (sell.makerProtocolFee > 0) {
                              uint makerProtocolFee = SafeMath.div(SafeMath.mul(sell.makerProtocolFee, price), INVERSE_BASIS_POINT);
                              if (sell.paymentToken == address(0)) {
                                  receiveAmount = SafeMath.sub(receiveAmount, makerProtocolFee);
                                  protocolFeeRecipient.transfer(makerProtocolFee);
                              } else {
                                  transferTokens(sell.paymentToken, sell.maker, protocolFeeRecipient, makerProtocolFee);
                              }
                          }
          
                          if (sell.takerProtocolFee > 0) {
                              uint takerProtocolFee = SafeMath.div(SafeMath.mul(sell.takerProtocolFee, price), INVERSE_BASIS_POINT);
                              if (sell.paymentToken == address(0)) {
                                  requiredAmount = SafeMath.add(requiredAmount, takerProtocolFee);
                                  protocolFeeRecipient.transfer(takerProtocolFee);
                              } else {
                                  transferTokens(sell.paymentToken, buy.maker, protocolFeeRecipient, takerProtocolFee);
                              }
                          }
          
                      } else {
                          /* Charge maker fee to seller. */
                          chargeProtocolFee(sell.maker, sell.feeRecipient, sell.makerRelayerFee);
          
                          /* Charge taker fee to buyer. */
                          chargeProtocolFee(buy.maker, sell.feeRecipient, sell.takerRelayerFee);
                      }
                  } else {
                      /* Buy-side order is maker. */
          
                      /* Assert taker fee is less than or equal to maximum fee specified by seller. */
                      require(buy.takerRelayerFee <= sell.takerRelayerFee);
          
                      if (sell.feeMethod == FeeMethod.SplitFee) {
                          /* 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.makerRelayerFee > 0) {
                              makerRelayerFee = SafeMath.div(SafeMath.mul(buy.makerRelayerFee, price), INVERSE_BASIS_POINT);
                              transferTokens(sell.paymentToken, buy.maker, buy.feeRecipient, makerRelayerFee);
                          }
          
                          if (buy.takerRelayerFee > 0) {
                              takerRelayerFee = SafeMath.div(SafeMath.mul(buy.takerRelayerFee, price), INVERSE_BASIS_POINT);
                              transferTokens(sell.paymentToken, sell.maker, buy.feeRecipient, takerRelayerFee);
                          }
          
                          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);
                          }
          
                      } else {
                          /* Charge maker fee to buyer. */
                          chargeProtocolFee(buy.maker, buy.feeRecipient, buy.makerRelayerFee);
                
                          /* Charge taker fee to seller. */
                          chargeProtocolFee(sell.maker, buy.feeRecipient, buy.takerRelayerFee);
                      }
                  }
          
                  if (sell.paymentToken == address(0)) {
                      /* Special-case Ether, order must be matched by buyer. */
                      require(msg.value >= requiredAmount);
                      sell.maker.transfer(receiveAmount);
                      /* Allow overshoot for variable-price auctions, refund difference. */
                      uint diff = SafeMath.sub(msg.value, requiredAmount);
                      if (diff > 0) {
                          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 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 == SaleKindInterface.Side.Buy && sell.side == SaleKindInterface.Side.Sell) &&     
                      /* Must use same fee method. */
                      (buy.feeMethod == sell.feeMethod) &&
                      /* 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 target. */
                      (buy.target == sell.target) &&
                      /* Must match howToCall. */
                      (buy.howToCall == sell.howToCall) &&
                      /* Buy-side order must be settleable. */
                      SaleKindInterface.canSettleOrder(buy.listingTime, buy.expirationTime) &&
                      /* Sell-side order must be settleable. */
                      SaleKindInterface.canSettleOrder(sell.listingTime, sell.expirationTime)
                  );
              }
          
              /**
               * @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, Sig memory buySig, Order memory sell, Sig memory sellSig, bytes32 metadata)
                  internal
                  reentrancyGuard
              {
                  /* CHECKS */
                
                  /* Ensure buy order validity and calculate hash if necessary. */
                  bytes32 buyHash;
                  if (buy.maker == msg.sender) {
                      require(validateOrderParameters(buy));
                  } else {
                      buyHash = requireValidOrder(buy, buySig);
                  }
          
                  /* Ensure sell order validity and calculate hash if necessary. */
                  bytes32 sellHash;
                  if (sell.maker == msg.sender) {
                      require(validateOrderParameters(sell));
                  } else {
                      sellHash = requireValidOrder(sell, sellSig);
                  }
                  
                  /* Must be matchable. */
                  require(ordersCanMatch(buy, sell));
          
                  /* Target must exist (prevent malicious selfdestructs just prior to order settlement). */
                  uint size;
                  address target = sell.target;
                  assembly {
                      size := extcodesize(target)
                  }
                  require(size > 0);
                
                  /* Must match calldata after replacement, if specified. */ 
                  if (buy.replacementPattern.length > 0) {
                    ArrayUtils.guardedArrayReplace(buy.calldata, sell.calldata, buy.replacementPattern);
                  }
                  if (sell.replacementPattern.length > 0) {
                    ArrayUtils.guardedArrayReplace(sell.calldata, buy.calldata, sell.replacementPattern);
                  }
                  require(ArrayUtils.arrayEq(buy.calldata, sell.calldata));
          
                  /* Retrieve delegateProxy contract. */
                  OwnableDelegateProxy delegateProxy = registry.proxies(sell.maker);
          
                  /* Proxy must exist. */
                  require(delegateProxy != address(0));
          
                  /* Assert implementation. */
                  require(delegateProxy.implementation() == registry.delegateProxyImplementation());
          
                  /* Access the passthrough AuthenticatedProxy. */
                  AuthenticatedProxy proxy = AuthenticatedProxy(delegateProxy);
          
                  /* 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. */
                  uint price = executeFundsTransfer(buy, sell);
          
                  /* Execute specified call through proxy. */
                  require(proxy.proxy(sell.target, sell.howToCall, sell.calldata));
          
                  /* Static calls are intentionally done after the effectful call so they can check resulting state. */
          
                  /* Handle buy-side static call if specified. */
                  if (buy.staticTarget != address(0)) {
                      require(staticCall(buy.staticTarget, sell.calldata, buy.staticExtradata));
                  }
          
                  /* Handle sell-side static call if specified. */
                  if (sell.staticTarget != address(0)) {
                      require(staticCall(sell.staticTarget, sell.calldata, sell.staticExtradata));
                  }
          
                  /* 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);
              }
          
          }
          
          contract Exchange is ExchangeCore {
          
              /**
               * @dev Call guardedArrayReplace - library function exposed for testing.
               */
              function guardedArrayReplace(bytes array, bytes desired, bytes mask)
                  public
                  pure
                  returns (bytes)
              {
                  ArrayUtils.guardedArrayReplace(array, desired, mask);
                  return array;
              }
          
              /**
               * Test copy byte array
               *
               * @param arrToCopy Array to copy
               * @return byte array
               */
              function testCopy(bytes arrToCopy)
                  public
                  pure
                  returns (bytes)
              {
                  bytes memory arr = new bytes(arrToCopy.length);
                  uint index;
                  assembly {
                      index := add(arr, 0x20)
                  }
                  ArrayUtils.unsafeWriteBytes(index, arrToCopy);
                  return arr;
              }
          
              /**
               * Test write address to bytes
               *
               * @param addr Address to write
               * @return byte array
               */
              function testCopyAddress(address addr)
                  public
                  pure
                  returns (bytes)
              {
                  bytes memory arr = new bytes(0x14);
                  uint index;
                  assembly {
                      index := add(arr, 0x20)
                  }
                  ArrayUtils.unsafeWriteAddress(index, addr);
                  return arr;
              }
          
              /**
               * @dev Call calculateFinalPrice - library function exposed for testing.
               */
              function calculateFinalPrice(SaleKindInterface.Side side, SaleKindInterface.SaleKind saleKind, uint basePrice, uint extra, uint listingTime, uint expirationTime)
                  public
                  view
                  returns (uint)
              {
                  return SaleKindInterface.calculateFinalPrice(side, saleKind, basePrice, extra, listingTime, expirationTime);
              }
          
              /**
               * @dev Call hashOrder - Solidity ABI encoding limitation workaround, hopefully temporary.
               */
              function hashOrder_(
                  address[7] addrs,
                  uint[9] uints,
                  FeeMethod feeMethod,
                  SaleKindInterface.Side side,
                  SaleKindInterface.SaleKind saleKind,
                  AuthenticatedProxy.HowToCall howToCall,
                  bytes calldata,
                  bytes replacementPattern,
                  bytes staticExtradata)
                  public
                  pure
                  returns (bytes32)
              {
                  return hashOrder(
                    Order(addrs[0], addrs[1], addrs[2], uints[0], uints[1], uints[2], uints[3], addrs[3], feeMethod, side, saleKind, addrs[4], howToCall, calldata, replacementPattern, addrs[5], staticExtradata, ERC20(addrs[6]), uints[4], uints[5], uints[6], uints[7], uints[8])
                  );
              }
          
              /**
               * @dev Call hashToSign - Solidity ABI encoding limitation workaround, hopefully temporary.
               */
              function hashToSign_(
                  address[7] addrs,
                  uint[9] uints,
                  FeeMethod feeMethod,
                  SaleKindInterface.Side side,
                  SaleKindInterface.SaleKind saleKind,
                  AuthenticatedProxy.HowToCall howToCall,
                  bytes calldata,
                  bytes replacementPattern,
                  bytes staticExtradata)
                  public
                  pure
                  returns (bytes32)
              { 
                  return hashToSign(
                    Order(addrs[0], addrs[1], addrs[2], uints[0], uints[1], uints[2], uints[3], addrs[3], feeMethod, side, saleKind, addrs[4], howToCall, calldata, replacementPattern, addrs[5], staticExtradata, ERC20(addrs[6]), uints[4], uints[5], uints[6], uints[7], uints[8])
                  );
              }
          
              /**
               * @dev Call validateOrderParameters - Solidity ABI encoding limitation workaround, hopefully temporary.
               */
              function validateOrderParameters_ (
                  address[7] addrs,
                  uint[9] uints,
                  FeeMethod feeMethod,
                  SaleKindInterface.Side side,
                  SaleKindInterface.SaleKind saleKind,
                  AuthenticatedProxy.HowToCall howToCall,
                  bytes calldata,
                  bytes replacementPattern,
                  bytes staticExtradata)
                  view
                  public
                  returns (bool)
              {
                  Order memory order = Order(addrs[0], addrs[1], addrs[2], uints[0], uints[1], uints[2], uints[3], addrs[3], feeMethod, side, saleKind, addrs[4], howToCall, calldata, replacementPattern, addrs[5], staticExtradata, ERC20(addrs[6]), uints[4], uints[5], uints[6], uints[7], uints[8]);
                  return validateOrderParameters(
                    order
                  );
              }
          
              /**
               * @dev Call validateOrder - Solidity ABI encoding limitation workaround, hopefully temporary.
               */
              function validateOrder_ (
                  address[7] addrs,
                  uint[9] uints,
                  FeeMethod feeMethod,
                  SaleKindInterface.Side side,
                  SaleKindInterface.SaleKind saleKind,
                  AuthenticatedProxy.HowToCall howToCall,
                  bytes calldata,
                  bytes replacementPattern,
                  bytes staticExtradata,
                  uint8 v,
                  bytes32 r,
                  bytes32 s)
                  view
                  public
                  returns (bool)
              {
                  Order memory order = Order(addrs[0], addrs[1], addrs[2], uints[0], uints[1], uints[2], uints[3], addrs[3], feeMethod, side, saleKind, addrs[4], howToCall, calldata, replacementPattern, addrs[5], staticExtradata, ERC20(addrs[6]), uints[4], uints[5], uints[6], uints[7], uints[8]);
                  return validateOrder(
                    hashToSign(order),
                    order,
                    Sig(v, r, s)
                  );
              }
          
              /**
               * @dev Call approveOrder - Solidity ABI encoding limitation workaround, hopefully temporary.
               */
              function approveOrder_ (
                  address[7] addrs,
                  uint[9] uints,
                  FeeMethod feeMethod,
                  SaleKindInterface.Side side,
                  SaleKindInterface.SaleKind saleKind,
                  AuthenticatedProxy.HowToCall howToCall,
                  bytes calldata,
                  bytes replacementPattern,
                  bytes staticExtradata,
                  bool orderbookInclusionDesired) 
                  public
              {
                  Order memory order = Order(addrs[0], addrs[1], addrs[2], uints[0], uints[1], uints[2], uints[3], addrs[3], feeMethod, side, saleKind, addrs[4], howToCall, calldata, replacementPattern, addrs[5], staticExtradata, ERC20(addrs[6]), uints[4], uints[5], uints[6], uints[7], uints[8]);
                  return approveOrder(order, orderbookInclusionDesired);
              }
          
              /**
               * @dev Call cancelOrder - Solidity ABI encoding limitation workaround, hopefully temporary.
               */
              function cancelOrder_(
                  address[7] addrs,
                  uint[9] uints,
                  FeeMethod feeMethod,
                  SaleKindInterface.Side side,
                  SaleKindInterface.SaleKind saleKind,
                  AuthenticatedProxy.HowToCall howToCall,
                  bytes calldata,
                  bytes replacementPattern,
                  bytes staticExtradata,
                  uint8 v,
                  bytes32 r,
                  bytes32 s)
                  public
              {
          
                  return cancelOrder(
                    Order(addrs[0], addrs[1], addrs[2], uints[0], uints[1], uints[2], uints[3], addrs[3], feeMethod, side, saleKind, addrs[4], howToCall, calldata, replacementPattern, addrs[5], staticExtradata, ERC20(addrs[6]), uints[4], uints[5], uints[6], uints[7], uints[8]),
                    Sig(v, r, s)
                  );
              }
          
              /**
               * @dev Call calculateCurrentPrice - Solidity ABI encoding limitation workaround, hopefully temporary.
               */
              function calculateCurrentPrice_(
                  address[7] addrs,
                  uint[9] uints,
                  FeeMethod feeMethod,
                  SaleKindInterface.Side side,
                  SaleKindInterface.SaleKind saleKind,
                  AuthenticatedProxy.HowToCall howToCall,
                  bytes calldata,
                  bytes replacementPattern,
                  bytes staticExtradata)
                  public
                  view
                  returns (uint)
              {
                  return calculateCurrentPrice(
                    Order(addrs[0], addrs[1], addrs[2], uints[0], uints[1], uints[2], uints[3], addrs[3], feeMethod, side, saleKind, addrs[4], howToCall, calldata, replacementPattern, addrs[5], staticExtradata, ERC20(addrs[6]), uints[4], uints[5], uints[6], uints[7], uints[8])
                  );
              }
          
              /**
               * @dev Call ordersCanMatch - Solidity ABI encoding limitation workaround, hopefully temporary.
               */
              function ordersCanMatch_(
                  address[14] addrs,
                  uint[18] uints,
                  uint8[8] feeMethodsSidesKindsHowToCalls,
                  bytes calldataBuy,
                  bytes calldataSell,
                  bytes replacementPatternBuy,
                  bytes replacementPatternSell,
                  bytes staticExtradataBuy,
                  bytes staticExtradataSell)
                  public
                  view
                  returns (bool)
              {
                  Order memory buy = Order(addrs[0], addrs[1], addrs[2], uints[0], uints[1], uints[2], uints[3], addrs[3], FeeMethod(feeMethodsSidesKindsHowToCalls[0]), SaleKindInterface.Side(feeMethodsSidesKindsHowToCalls[1]), SaleKindInterface.SaleKind(feeMethodsSidesKindsHowToCalls[2]), addrs[4], AuthenticatedProxy.HowToCall(feeMethodsSidesKindsHowToCalls[3]), calldataBuy, replacementPatternBuy, addrs[5], staticExtradataBuy, ERC20(addrs[6]), uints[4], uints[5], uints[6], uints[7], uints[8]);
                  Order memory sell = Order(addrs[7], addrs[8], addrs[9], uints[9], uints[10], uints[11], uints[12], addrs[10], FeeMethod(feeMethodsSidesKindsHowToCalls[4]), SaleKindInterface.Side(feeMethodsSidesKindsHowToCalls[5]), SaleKindInterface.SaleKind(feeMethodsSidesKindsHowToCalls[6]), addrs[11], AuthenticatedProxy.HowToCall(feeMethodsSidesKindsHowToCalls[7]), calldataSell, replacementPatternSell, addrs[12], staticExtradataSell, ERC20(addrs[13]), uints[13], uints[14], uints[15], uints[16], uints[17]);
                  return ordersCanMatch(
                    buy,
                    sell
                  );
              }
          
              /**
               * @dev Return whether or not two orders' calldata specifications can match
               * @param buyCalldata Buy-side order calldata
               * @param buyReplacementPattern Buy-side order calldata replacement mask
               * @param sellCalldata Sell-side order calldata
               * @param sellReplacementPattern Sell-side order calldata replacement mask
               * @return Whether the orders' calldata can be matched
               */
              function orderCalldataCanMatch(bytes buyCalldata, bytes buyReplacementPattern, bytes sellCalldata, bytes sellReplacementPattern)
                  public
                  pure
                  returns (bool)
              {
                  if (buyReplacementPattern.length > 0) {
                    ArrayUtils.guardedArrayReplace(buyCalldata, sellCalldata, buyReplacementPattern);
                  }
                  if (sellReplacementPattern.length > 0) {
                    ArrayUtils.guardedArrayReplace(sellCalldata, buyCalldata, sellReplacementPattern);
                  }
                  return ArrayUtils.arrayEq(buyCalldata, sellCalldata);
              }
          
              /**
               * @dev Call calculateMatchPrice - Solidity ABI encoding limitation workaround, hopefully temporary.
               */
              function calculateMatchPrice_(
                  address[14] addrs,
                  uint[18] uints,
                  uint8[8] feeMethodsSidesKindsHowToCalls,
                  bytes calldataBuy,
                  bytes calldataSell,
                  bytes replacementPatternBuy,
                  bytes replacementPatternSell,
                  bytes staticExtradataBuy,
                  bytes staticExtradataSell)
                  public
                  view
                  returns (uint)
              {
                  Order memory buy = Order(addrs[0], addrs[1], addrs[2], uints[0], uints[1], uints[2], uints[3], addrs[3], FeeMethod(feeMethodsSidesKindsHowToCalls[0]), SaleKindInterface.Side(feeMethodsSidesKindsHowToCalls[1]), SaleKindInterface.SaleKind(feeMethodsSidesKindsHowToCalls[2]), addrs[4], AuthenticatedProxy.HowToCall(feeMethodsSidesKindsHowToCalls[3]), calldataBuy, replacementPatternBuy, addrs[5], staticExtradataBuy, ERC20(addrs[6]), uints[4], uints[5], uints[6], uints[7], uints[8]);
                  Order memory sell = Order(addrs[7], addrs[8], addrs[9], uints[9], uints[10], uints[11], uints[12], addrs[10], FeeMethod(feeMethodsSidesKindsHowToCalls[4]), SaleKindInterface.Side(feeMethodsSidesKindsHowToCalls[5]), SaleKindInterface.SaleKind(feeMethodsSidesKindsHowToCalls[6]), addrs[11], AuthenticatedProxy.HowToCall(feeMethodsSidesKindsHowToCalls[7]), calldataSell, replacementPatternSell, addrs[12], staticExtradataSell, ERC20(addrs[13]), uints[13], uints[14], uints[15], uints[16], uints[17]);
                  return calculateMatchPrice(
                    buy,
                    sell
                  );
              }
          
              /**
               * @dev Call atomicMatch - Solidity ABI encoding limitation workaround, hopefully temporary.
               */
              function atomicMatch_(
                  address[14] addrs,
                  uint[18] uints,
                  uint8[8] feeMethodsSidesKindsHowToCalls,
                  bytes calldataBuy,
                  bytes calldataSell,
                  bytes replacementPatternBuy,
                  bytes replacementPatternSell,
                  bytes staticExtradataBuy,
                  bytes staticExtradataSell,
                  uint8[2] vs,
                  bytes32[5] rssMetadata)
                  public
                  payable
              {
          
                  return atomicMatch(
                    Order(addrs[0], addrs[1], addrs[2], uints[0], uints[1], uints[2], uints[3], addrs[3], FeeMethod(feeMethodsSidesKindsHowToCalls[0]), SaleKindInterface.Side(feeMethodsSidesKindsHowToCalls[1]), SaleKindInterface.SaleKind(feeMethodsSidesKindsHowToCalls[2]), addrs[4], AuthenticatedProxy.HowToCall(feeMethodsSidesKindsHowToCalls[3]), calldataBuy, replacementPatternBuy, addrs[5], staticExtradataBuy, ERC20(addrs[6]), uints[4], uints[5], uints[6], uints[7], uints[8]),
                    Sig(vs[0], rssMetadata[0], rssMetadata[1]),
                    Order(addrs[7], addrs[8], addrs[9], uints[9], uints[10], uints[11], uints[12], addrs[10], FeeMethod(feeMethodsSidesKindsHowToCalls[4]), SaleKindInterface.Side(feeMethodsSidesKindsHowToCalls[5]), SaleKindInterface.SaleKind(feeMethodsSidesKindsHowToCalls[6]), addrs[11], AuthenticatedProxy.HowToCall(feeMethodsSidesKindsHowToCalls[7]), calldataSell, replacementPatternSell, addrs[12], staticExtradataSell, ERC20(addrs[13]), uints[13], uints[14], uints[15], uints[16], uints[17]),
                    Sig(vs[1], rssMetadata[2], rssMetadata[3]),
                    rssMetadata[4]
                  );
              }
          
          }
          
          contract WyvernExchange is Exchange {
          
              string public constant name = "Project Wyvern Exchange";
          
              string public constant version = "2.2";
          
              string public constant codename = "Lambton Worm";
          
              /**
               * @dev Initialize a WyvernExchange instance
               * @param registryAddress Address of the registry instance which this Exchange instance will use
               * @param tokenAddress Address of the token used for protocol fees
               */
              constructor (ProxyRegistry registryAddress, TokenTransferProxy tokenTransferProxyAddress, ERC20 tokenAddress, address protocolFeeAddress) public {
                  registry = registryAddress;
                  tokenTransferProxy = tokenTransferProxyAddress;
                  exchangeToken = tokenAddress;
                  protocolFeeRecipient = protocolFeeAddress;
                  owner = msg.sender;
              }
          
          }
          
          library SaleKindInterface {
          
              /**
               * Side: buy or sell.
               */
              enum Side { Buy, Sell }
          
              /**
               * Currently supported kinds of sale: fixed price, Dutch auction. 
               * English auctions cannot be supported without stronger escrow guarantees.
               * Future interesting options: Vickrey auction, nonlinear Dutch auctions.
               */
              enum SaleKind { FixedPrice, DutchAuction }
          
              /**
               * @dev Check whether the parameters of a sale are valid
               * @param saleKind Kind of sale
               * @param expirationTime Order expiration time
               * @return Whether the parameters were valid
               */
              function validateParameters(SaleKind saleKind, uint expirationTime)
                  pure
                  internal
                  returns (bool)
              {
                  /* Auctions must have a set expiration date. */
                  return (saleKind == SaleKind.FixedPrice || expirationTime > 0);
              }
          
              /**
               * @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(uint listingTime, uint expirationTime)
                  view
                  internal
                  returns (bool)
              {
                  return (listingTime < now) && (expirationTime == 0 || now < expirationTime);
              }
          
              /**
               * @dev Calculate the settlement price of an order
               * @dev Precondition: parameters have passed validateParameters.
               * @param side Order side
               * @param saleKind Method of sale
               * @param basePrice Order base price
               * @param extra Order extra price data
               * @param listingTime Order listing time
               * @param expirationTime Order expiration time
               */
              function calculateFinalPrice(Side side, SaleKind saleKind, uint basePrice, uint extra, uint listingTime, uint expirationTime)
                  view
                  internal
                  returns (uint finalPrice)
              {
                  if (saleKind == SaleKind.FixedPrice) {
                      return basePrice;
                  } else if (saleKind == SaleKind.DutchAuction) {
                      uint diff = SafeMath.div(SafeMath.mul(extra, SafeMath.sub(now, listingTime)), SafeMath.sub(expirationTime, listingTime));
                      if (side == Side.Sell) {
                          /* Sell-side - start price: basePrice. End price: basePrice - extra. */
                          return SafeMath.sub(basePrice, diff);
                      } else {
                          /* Buy-side - start price: basePrice. End price: basePrice + extra. */
                          return SafeMath.add(basePrice, diff);
                      }
                  }
              }
          
          }
          
          contract ProxyRegistry is Ownable {
          
              /* DelegateProxy implementation contract. Must be initialized. */
              address public delegateProxyImplementation;
          
              /* Authenticated proxies by user. */
              mapping(address => OwnableDelegateProxy) public proxies;
          
              /* Contracts pending access. */
              mapping(address => uint) public pending;
          
              /* Contracts allowed to call those proxies. */
              mapping(address => bool) public contracts;
          
              /* Delay period for adding an authenticated contract.
                 This mitigates a particular class of potential attack on the Wyvern DAO (which owns this registry) - if at any point the value of assets held by proxy contracts exceeded the value of half the WYV supply (votes in the DAO),
                 a malicious but rational attacker could buy half the Wyvern and grant themselves access to all the proxy contracts. A delay period renders this attack nonthreatening - given two weeks, if that happened, users would have
                 plenty of time to notice and transfer their assets.
              */
              uint public DELAY_PERIOD = 2 weeks;
          
              /**
               * Start the process to enable access for specified contract. Subject to delay period.
               *
               * @dev ProxyRegistry owner only
               * @param addr Address to which to grant permissions
               */
              function startGrantAuthentication (address addr)
                  public
                  onlyOwner
              {
                  require(!contracts[addr] && pending[addr] == 0);
                  pending[addr] = now;
              }
          
              /**
               * End the process to nable access for specified contract after delay period has passed.
               *
               * @dev ProxyRegistry owner only
               * @param addr Address to which to grant permissions
               */
              function endGrantAuthentication (address addr)
                  public
                  onlyOwner
              {
                  require(!contracts[addr] && pending[addr] != 0 && ((pending[addr] + DELAY_PERIOD) < now));
                  pending[addr] = 0;
                  contracts[addr] = true;
              }
          
              /**
               * Revoke access for specified contract. Can be done instantly.
               *
               * @dev ProxyRegistry owner only
               * @param addr Address of which to revoke permissions
               */    
              function revokeAuthentication (address addr)
                  public
                  onlyOwner
              {
                  contracts[addr] = false;
              }
          
              /**
               * Register a proxy contract with this registry
               *
               * @dev Must be called by the user which the proxy is for, creates a new AuthenticatedProxy
               * @return New AuthenticatedProxy contract
               */
              function registerProxy()
                  public
                  returns (OwnableDelegateProxy proxy)
              {
                  require(proxies[msg.sender] == address(0));
                  proxy = new OwnableDelegateProxy(msg.sender, delegateProxyImplementation, abi.encodeWithSignature("initialize(address,address)", msg.sender, address(this)));
                  proxies[msg.sender] = proxy;
                  return proxy;
              }
          
          }
          
          contract TokenTransferProxy {
          
              /* Authentication registry. */
              ProxyRegistry public registry;
          
              /**
               * Call ERC20 `transferFrom`
               *
               * @dev Authenticated contract only
               * @param token ERC20 token address
               * @param from From address
               * @param to To address
               * @param amount Transfer amount
               */
              function transferFrom(address token, address from, address to, uint amount)
                  public
                  returns (bool)
              {
                  require(registry.contracts(msg.sender));
                  return ERC20(token).transferFrom(from, to, amount);
              }
          
          }
          
          contract OwnedUpgradeabilityStorage {
          
            // Current implementation
            address internal _implementation;
          
            // Owner of the contract
            address private _upgradeabilityOwner;
          
            /**
             * @dev Tells the address of the owner
             * @return the address of the owner
             */
            function upgradeabilityOwner() public view returns (address) {
              return _upgradeabilityOwner;
            }
          
            /**
             * @dev Sets the address of the owner
             */
            function setUpgradeabilityOwner(address newUpgradeabilityOwner) internal {
              _upgradeabilityOwner = newUpgradeabilityOwner;
            }
          
            /**
            * @dev Tells the address of the current implementation
            * @return address of the current implementation
            */
            function implementation() public view returns (address) {
              return _implementation;
            }
          
            /**
            * @dev Tells the proxy type (EIP 897)
            * @return Proxy type, 2 for forwarding proxy
            */
            function proxyType() public pure returns (uint256 proxyTypeId) {
              return 2;
            }
          }
          
          contract AuthenticatedProxy is TokenRecipient, OwnedUpgradeabilityStorage {
          
              /* Whether initialized. */
              bool initialized = false;
          
              /* Address which owns this proxy. */
              address public user;
          
              /* Associated registry with contract authentication information. */
              ProxyRegistry public registry;
          
              /* Whether access has been revoked. */
              bool public revoked;
          
              /* Delegate call could be used to atomically transfer multiple assets owned by the proxy contract with one order. */
              enum HowToCall { Call, DelegateCall }
          
              /* Event fired when the proxy access is revoked or unrevoked. */
              event Revoked(bool revoked);
          
              /**
               * Initialize an AuthenticatedProxy
               *
               * @param addrUser Address of user on whose behalf this proxy will act
               * @param addrRegistry Address of ProxyRegistry contract which will manage this proxy
               */
              function initialize (address addrUser, ProxyRegistry addrRegistry)
                  public
              {
                  require(!initialized);
                  initialized = true;
                  user = addrUser;
                  registry = addrRegistry;
              }
          
              /**
               * Set the revoked flag (allows a user to revoke ProxyRegistry access)
               *
               * @dev Can be called by the user only
               * @param revoke Whether or not to revoke access
               */
              function setRevoke(bool revoke)
                  public
              {
                  require(msg.sender == user);
                  revoked = revoke;
                  emit Revoked(revoke);
              }
          
              /**
               * Execute a message call from the proxy contract
               *
               * @dev Can be called by the user, or by a contract authorized by the registry as long as the user has not revoked access
               * @param dest Address to which the call will be sent
               * @param howToCall Which kind of call to make
               * @param calldata Calldata to send
               * @return Result of the call (success or failure)
               */
              function proxy(address dest, HowToCall howToCall, bytes calldata)
                  public
                  returns (bool result)
              {
                  require(msg.sender == user || (!revoked && registry.contracts(msg.sender)));
                  if (howToCall == HowToCall.Call) {
                      result = dest.call(calldata);
                  } else if (howToCall == HowToCall.DelegateCall) {
                      result = dest.delegatecall(calldata);
                  }
                  return result;
              }
          
              /**
               * Execute a message call and assert success
               * 
               * @dev Same functionality as `proxy`, just asserts the return value
               * @param dest Address to which the call will be sent
               * @param howToCall What kind of call to make
               * @param calldata Calldata to send
               */
              function proxyAssert(address dest, HowToCall howToCall, bytes calldata)
                  public
              {
                  require(proxy(dest, howToCall, calldata));
              }
          
          }
          
          contract Proxy {
          
            /**
            * @dev Tells the address of the implementation where every call will be delegated.
            * @return address of the implementation to which it will be delegated
            */
            function implementation() public view returns (address);
          
            /**
            * @dev Tells the type of proxy (EIP 897)
            * @return Type of proxy, 2 for upgradeable proxy
            */
            function proxyType() public pure returns (uint256 proxyTypeId);
          
            /**
            * @dev Fallback function allowing to perform a delegatecall to the given implementation.
            * This function will return whatever the implementation call returns
            */
            function () payable public {
              address _impl = implementation();
              require(_impl != address(0));
          
              assembly {
                let ptr := mload(0x40)
                calldatacopy(ptr, 0, calldatasize)
                let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0)
                let size := returndatasize
                returndatacopy(ptr, 0, size)
          
                switch result
                case 0 { revert(ptr, size) }
                default { return(ptr, size) }
              }
            }
          }
          
          contract OwnedUpgradeabilityProxy is Proxy, OwnedUpgradeabilityStorage {
            /**
            * @dev Event to show ownership has been transferred
            * @param previousOwner representing the address of the previous owner
            * @param newOwner representing the address of the new owner
            */
            event ProxyOwnershipTransferred(address previousOwner, address newOwner);
          
            /**
            * @dev This event will be emitted every time the implementation gets upgraded
            * @param implementation representing the address of the upgraded implementation
            */
            event Upgraded(address indexed implementation);
          
            /**
            * @dev Upgrades the implementation address
            * @param implementation representing the address of the new implementation to be set
            */
            function _upgradeTo(address implementation) internal {
              require(_implementation != implementation);
              _implementation = implementation;
              emit Upgraded(implementation);
            }
          
            /**
            * @dev Throws if called by any account other than the owner.
            */
            modifier onlyProxyOwner() {
              require(msg.sender == proxyOwner());
              _;
            }
          
            /**
             * @dev Tells the address of the proxy owner
             * @return the address of the proxy owner
             */
            function proxyOwner() public view returns (address) {
              return upgradeabilityOwner();
            }
          
            /**
             * @dev Allows the current owner to transfer control of the contract to a newOwner.
             * @param newOwner The address to transfer ownership to.
             */
            function transferProxyOwnership(address newOwner) public onlyProxyOwner {
              require(newOwner != address(0));
              emit ProxyOwnershipTransferred(proxyOwner(), newOwner);
              setUpgradeabilityOwner(newOwner);
            }
          
            /**
             * @dev Allows the upgradeability owner to upgrade the current implementation of the proxy.
             * @param implementation representing the address of the new implementation to be set.
             */
            function upgradeTo(address implementation) public onlyProxyOwner {
              _upgradeTo(implementation);
            }
          
            /**
             * @dev Allows the upgradeability owner to upgrade the current implementation of the proxy
             * and delegatecall the new implementation for initialization.
             * @param implementation representing the address of the new implementation to be set.
             * @param data represents the msg.data to bet sent in the low level call. This parameter may include the function
             * signature of the implementation to be called with the needed payload
             */
            function upgradeToAndCall(address implementation, bytes data) payable public onlyProxyOwner {
              upgradeTo(implementation);
              require(address(this).delegatecall(data));
            }
          }
          
          contract OwnableDelegateProxy is OwnedUpgradeabilityProxy {
          
              constructor(address owner, address initialImplementation, bytes calldata)
                  public
              {
                  setUpgradeabilityOwner(owner);
                  _upgradeTo(initialImplementation);
                  require(initialImplementation.delegatecall(calldata));
              }
          
          }

          File 2 of 5: PersonalityPodERC721
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.7;
          import "./RoyalNFT.sol";
          /**
           * @title Personality Pod, a.k.a. AI Personality
           *
           * @notice Personality Pod replaces AI Pod in version 2 release, it doesn't
           *      store any metadata on-chain, all the token related data except URI
           *      (rarity, traits, etc.) is expected to be stored off-chain
           *
           * @notice Terms Personality Pod and AI Personality have identical meaning and
           *      used interchangeably all over the code, documentation, scripts, etc.
           *
           * @dev Personality Pod is a Tiny ERC721, it supports minting and burning,
           *      its token ID space is limited to 32 bits
           */
          contract PersonalityPodERC721 is RoyalNFT {
          \t/**
          \t * @inheritdoc TinyERC721
          \t */
          \tuint256 public constant override TOKEN_UID = 0xd9b5d3b66c60255ffa16c57c0f1b2db387997fa02af673da5767f1acb0f345af;
          \t/**
          \t * @dev Constructs/deploys AI Personality instance
          \t *      with the name and symbol defined during the deployment
          \t */
          \tconstructor(string memory _name, string memory _symbol) RoyalNFT(_name, _symbol) {}
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.7;
          import "../interfaces/EIP2981Spec.sol";
          import "./TinyERC721.sol";
          /**
           * @title Royal NFT
           *
           * @dev Supports EIP-2981 royalties on NFT secondary sales
           *
           * @dev Supports OpenSea contract metadata royalties
           *
           * @dev Introduces "owner" to support OpenSea collections
           */
          abstract contract RoyalNFT is EIP2981, TinyERC721 {
          \t/**
          \t * @dev OpenSea expects NFTs to be "Ownable", that is having an "owner",
          \t *      we introduce a fake "owner" here with no authority
          \t */
          \taddress public owner;
          \t/**
          \t * @dev Constructs/deploys ERC721 with EIP-2981 instance with the name and symbol specified
          \t *
          \t * @param _name name of the token to be accessible as `name()`,
          \t *      ERC-20 compatible descriptive name for a collection of NFTs in this contract
          \t * @param _symbol token symbol to be accessible as `symbol()`,
          \t *      ERC-20 compatible descriptive name for a collection of NFTs in this contract
          \t */
          \tconstructor(string memory _name, string memory _symbol) TinyERC721(_name, _symbol) {
          \t\t// initialize the "owner" as a deployer account
          \t\towner = msg.sender;
          \t}
          \t/**
          \t * @dev Fired in setContractURI()
          \t *
          \t * @param _by an address which executed update
          \t * @param _oldVal old contractURI value
          \t * @param _newVal new contractURI value
          \t */
          \tevent ContractURIUpdated(address indexed _by, string _oldVal, string _newVal);
          \t/**
          \t * @dev Fired in setRoyaltyInfo()
          \t *
          \t * @param _by an address which executed update
          \t * @param _oldReceiver old royaltyReceiver value
          \t * @param _newReceiver new royaltyReceiver value
          \t * @param _oldPercentage old royaltyPercentage value
          \t * @param _newPercentage new royaltyPercentage value
          \t */
          \tevent RoyaltyInfoUpdated(
          \t\taddress indexed _by,
          \t\taddress indexed _oldReceiver,
          \t\taddress indexed _newReceiver,
          \t\tuint16 _oldPercentage,
          \t\tuint16 _newPercentage
          \t);
          \t/**
          \t * @dev Fired in setOwner()
          \t *
          \t * @param _by an address which set the new "owner"
          \t * @param _oldVal previous "owner" address
          \t * @param _newVal new "owner" address
          \t */
          \tevent OwnerUpdated(address indexed _by, address indexed _oldVal, address indexed _newVal);
          \t/**
          \t * @notice Royalty manager is responsible for managing the EIP2981 royalty info
          \t *
          \t * @dev Role ROLE_ROYALTY_MANAGER allows updating the royalty information
          \t *      (executing `setRoyaltyInfo` function)
          \t */
          \tuint32 public constant ROLE_ROYALTY_MANAGER = 0x0020_0000;
          \t/**
          \t * @notice Owner manager is responsible for setting/updating an "owner" field
          \t *
          \t * @dev Role ROLE_OWNER_MANAGER allows updating the "owner" field
          \t *      (executing `setOwner` function)
          \t */
          \tuint32 public constant ROLE_OWNER_MANAGER = 0x0040_0000;
          \t/**
          \t * @notice Address to receive EIP-2981 royalties from secondary sales
          \t *         see https://eips.ethereum.org/EIPS/eip-2981
          \t */
          \taddress public royaltyReceiver = address(0x379e2119f6e0D6088537da82968e2a7ea178dDcF);
          \t/**
          \t * @notice Percentage of token sale price to be used for EIP-2981 royalties from secondary sales
          \t *         see https://eips.ethereum.org/EIPS/eip-2981
          \t *
          \t * @dev Has 2 decimal precision. E.g. a value of 500 would result in a 5% royalty fee
          \t */
          \tuint16 public royaltyPercentage = 750;
          \t/**
          \t * @notice Contract level metadata to define collection name, description, and royalty fees.
          \t *         see https://docs.opensea.io/docs/contract-level-metadata
          \t *
          \t * @dev Should be overwritten by inheriting contracts. By default only includes royalty information
          \t */
          \tstring public contractURI = "https://gateway.pinata.cloud/ipfs/QmU92w8iKpcaabCoyHtMg7iivWGqW2gW1hgARDtqCmJUWv";
          \t/**
          \t * @dev Restricted access function which updates the contract uri
          \t *
          \t * @dev Requires executor to have ROLE_URI_MANAGER permission
          \t *
          \t * @param _contractURI new contract URI to set
          \t */
          \tfunction setContractURI(string memory _contractURI) public {
          \t\t// verify the access permission
          \t\trequire(isSenderInRole(ROLE_URI_MANAGER), "access denied");
          \t\t// emit an event first - to log both old and new values
          \t\temit ContractURIUpdated(msg.sender, contractURI, _contractURI);
          \t\t// update the contract URI
          \t\tcontractURI = _contractURI;
          \t}
          \t/**
          \t * @notice EIP-2981 function to calculate royalties for sales in secondary marketplaces.
          \t *         see https://eips.ethereum.org/EIPS/eip-2981
          \t *
          \t * @param _tokenId the token id to calculate royalty info for
          \t * @param _salePrice the price (in any unit, .e.g wei, ERC20 token, et.c.) of the token to be sold
          \t *
          \t * @return receiver the royalty receiver
          \t * @return royaltyAmount royalty amount in the same unit as _salePrice
          \t */
          \tfunction royaltyInfo(
          \t\tuint256 _tokenId,
          \t\tuint256 _salePrice
          \t) external view override returns (
          \t\taddress receiver,
          \t\tuint256 royaltyAmount
          \t) {
          \t\t// simply calculate the values and return the result
          \t\treturn (royaltyReceiver, _salePrice * royaltyPercentage / 100_00);
          \t}
          \t/**
          \t * @dev Restricted access function which updates the royalty info
          \t *
          \t * @dev Requires executor to have ROLE_ROYALTY_MANAGER permission
          \t *
          \t * @param _royaltyReceiver new royalty receiver to set
          \t * @param _royaltyPercentage new royalty percentage to set
          \t */
          \tfunction setRoyaltyInfo(
          \t\taddress _royaltyReceiver,
          \t\tuint16 _royaltyPercentage
          \t) public {
          \t\t// verify the access permission
          \t\trequire(isSenderInRole(ROLE_ROYALTY_MANAGER), "access denied");
          \t\t// verify royalty percentage is zero if receiver is also zero
          \t\trequire(_royaltyReceiver != address(0) || _royaltyPercentage == 0, "invalid receiver");
          \t\t// emit an event first - to log both old and new values
          \t\temit RoyaltyInfoUpdated(
          \t\t\tmsg.sender,
          \t\t\troyaltyReceiver,
          \t\t\t_royaltyReceiver,
          \t\t\troyaltyPercentage,
          \t\t\t_royaltyPercentage
          \t\t);
          \t\t// update the values
          \t\troyaltyReceiver = _royaltyReceiver;
          \t\troyaltyPercentage = _royaltyPercentage;
          \t}
          \t/**
          \t * @notice Checks if the address supplied is an "owner" of the smart contract
          \t *      Note: an "owner" doesn't have any authority on the smart contract and is "nominal"
          \t *
          \t * @return true if the caller is the current owner.
          \t */
          \tfunction isOwner(address _addr) public view returns(bool) {
          \t\t// just evaluate and return the result
          \t\treturn _addr == owner;
          \t}
          \t/**
          \t * @dev Restricted access function to set smart contract "owner"
          \t *      Note: an "owner" set doesn't have any authority, and cannot even update "owner"
          \t *
          \t * @dev Requires executor to have ROLE_OWNER_MANAGER permission
          \t *
          \t * @param _owner new "owner" of the smart contract
          \t */
          \tfunction transferOwnership(address _owner) public {
          \t\t// verify the access permission
          \t\trequire(isSenderInRole(ROLE_OWNER_MANAGER), "access denied");
          \t\t// emit an event first - to log both old and new values
          \t\temit OwnerUpdated(msg.sender, owner, _owner);
          \t\t// update "owner"
          \t\towner = _owner;
          \t}
          \t/**
          \t * @inheritdoc ERC165
          \t */
          \tfunction supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, TinyERC721) returns (bool) {
          \t\t// construct the interface support from EIP-2981 and super interfaces
          \t\treturn interfaceId == type(EIP2981).interfaceId || super.supportsInterface(interfaceId);
          \t}
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.7;
          import "./ERC165Spec.sol";
          ///
          /// @dev Interface for the NFT Royalty Standard
          ///
          interface EIP2981 is ERC165 {
          \t/// ERC165 bytes to add to interface array - set in parent contract
          \t/// implementing this standard
          \t///
          \t/// bytes4(keccak256("royaltyInfo(uint256,uint256)")) == 0x2a55205a
          \t/// bytes4 private constant _INTERFACE_ID_ERC2981 = 0x2a55205a;
          \t/// _registerInterface(_INTERFACE_ID_ERC2981);
          \t/// @notice Called with the sale price to determine how much royalty
          \t//          is owed and to whom.
          \t/// @param _tokenId - the NFT asset queried for royalty information
          \t/// @param _salePrice - the sale price of the NFT asset specified by _tokenId
          \t/// @return receiver - address of who should be sent the royalty payment
          \t/// @return royaltyAmount - the royalty payment amount for _salePrice
          \tfunction royaltyInfo(
          \t\tuint256 _tokenId,
          \t\tuint256 _salePrice
          \t) external view returns (
          \t\taddress receiver,
          \t\tuint256 royaltyAmount
          \t);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.7;
          import "../interfaces/ERC721Spec.sol";
          import "../interfaces/AletheaERC721Spec.sol";
          import "../lib/AddressUtils.sol";
          import "../lib/ArrayUtils.sol";
          import "../lib/StringUtils.sol";
          import "../lib/ECDSA.sol";
          import "../utils/AccessControl.sol";
          /**
           * @title Tiny ERC721
           *
           * @notice Tiny ERC721 defines an NFT with a very small (up to 32 bits) ID space.
           *      ERC721 enumeration support requires additional writes to the storage:
           *      - when transferring a token in order to update the NFT collections of
           *        the previous and next owners,
           *      - when minting/burning a token in order to update global NFT collection
           *
           * @notice Reducing NFT ID space to 32 bits allows
           *      - to eliminate the need to have and to write to two additional storage mappings
           *        (also achievable with the 48 bits ID space)
           *      - for batch minting optimization by writing 8 tokens instead of 5 at once into
           *        global/local collections
           *
           * @notice This smart contract is designed to be inherited by concrete implementations,
           *      which are expected to define token metadata, auxiliary functions to access the metadata,
           *      and explicitly define token minting interface, which should be built on top
           *      of current smart contract internal interface
           *
           * @notice Fully ERC721-compatible with all optional interfaces implemented (metadata, enumeration),
           *      see https://eips.ethereum.org/EIPS/eip-721
           *
           * @dev ERC721: contract has passed adopted OpenZeppelin ERC721 tests
           *        https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/test/token/ERC721/ERC721.behavior.js
           *        https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/test/token/ERC721/extensions/ERC721URIStorage.test.js
           *
           * @dev A note on token URI: there are major differences on how token URI behaves comparing to Zeppelin impl:
           *      1. A token URI can be set for non-existing token for pre-allocation purposes,
           *         still the URI will be deleted once token is burnt
           *      2. If token URI is set, base URI has no affect on the token URI, the two are not concatenated,
           *         base URI is used to construct the token URI only if the latter was not explicitly set
           *
           * @dev Supports EIP-712 powered permits - permit() - approve() with signature.
           *      Supports EIP-712 powered operator permits - permitForAll() - setApprovalForAll() with signature.
           *
           * @dev EIP712 Domain:
           *      name: AliERC721v1
           *      version: not in use, omitted (name already contains version)
           *      chainId: EIP-155 chain id
           *      verifyingContract: deployed contract address
           *      salt: permitNonces[owner], where owner is an address which allows operation on their tokens
           *
           * @dev Permit type:
           *      owner: address
           *      operator: address
           *      tokenId: uint256
           *      nonce: uint256
           *      deadline: uint256
           *
           * @dev Permit typeHash:
           *        keccak256("Permit(address owner,address operator,uint256 tokenId,uint256 nonce,uint256 deadline)")
           *
           * @dev PermitForAll type:
           *      owner: address
           *      operator: address
           *      approved: bool
           *      nonce: uint256
           *      deadline: uint256
           *
           * @dev PermitForAll typeHash:
           *        keccak256("PermitForAll(address owner,address operator,bool approved,uint256 nonce,uint256 deadline)")
           *
           * @dev See https://eips.ethereum.org/EIPS/eip-712
           * @dev See usage examples in tests: erc721_permits.js
           */
          abstract contract TinyERC721 is ERC721Enumerable, ERC721Metadata, WithBaseURI, MintableERC721, BurnableERC721, AccessControl {
          \t// enable push32 optimization for uint32[]
          \tusing ArrayUtils for uint32[];
          \t/**
          \t * @dev Smart contract unique identifier, a random number
          \t *
          \t * @dev Should be regenerated each time smart contact source code is changed
          \t *      and changes smart contract itself is to be redeployed
          \t *
          \t * @dev Generated using https://www.random.org/bytes/
          \t * @dev Example value: 0xdbdd2b4ff38a8516da0b8e7ae93288b5e2fed0c92fb051cee90ccf4e4ec9736e
          \t */
          \tfunction TOKEN_UID() external view virtual returns(uint256);
          \t/**
          \t * @notice ERC-20 compatible descriptive name for a collection of NFTs in this contract
          \t *
          \t * @inheritdoc ERC721Metadata
          \t */
          \tstring public override name;
          \t/**
          \t * @notice ERC-20 compatible abbreviated name for a collection of NFTs in this contract
          \t *
          \t * @inheritdoc ERC721Metadata
          \t */
          \tstring public override symbol;
          \t/**
          \t * @notice Current implementation includes a function `decimals` that returns uint8(0)
          \t *      to be more compatible with ERC-20
          \t *
          \t * @dev ERC20 compliant token decimals is equal to zero since ERC721 token is non-fungible
          \t *      and therefore non-divisible
          \t */
          \tuint8 public constant decimals = 0;
          \t/**
          \t * @notice Ownership information for all the tokens in existence
          \t *
          \t * @dev Maps `Token ID => Token ID Global Index | Token ID Local Index | Token Owner Address`, where
          \t *      - Token ID Global Index denotes Token ID index in the array of all the tokens,
          \t *      - Token ID Local Index denotes Token ID index in the array of all the tokens owned by the owner,
          \t *      - Token ID indexes are 32 bits long,
          \t *      - `|` denotes bitwise concatenation of the values
          \t * @dev Token Owner Address for a given Token ID is lower 160 bits of the mapping value
          \t */
          \tmapping(uint256 => uint256) internal tokens;
          \t/**
          \t * @notice Enumerated collections of the tokens owned by particular owners
          \t *
          \t * @dev We call these collections "Local" token collections
          \t *
          \t * @dev Maps `Token Owner Address => Owned Token IDs Array`
          \t *
          \t * @dev Token owner balance is the length of their token collection:
          \t *      `balanceOf(owner) = collections[owner].length`
          \t */
          \tmapping(address => uint32[]) internal collections;
          \t/**
          \t * @notice An array of all the tokens in existence
          \t *
          \t * @dev We call this collection "Global" token collection
          \t *
          \t * @dev Array with all Token IDs, used for enumeration
          \t *
          \t * @dev Total token supply `tokenSupply` is the length of this collection:
          \t *      `totalSupply() = allTokens.length`
          \t */
          \tuint32[] internal allTokens;
          \t/**
          \t * @notice Addresses approved by token owners to transfer their tokens
          \t *
          \t * @dev `Maps Token ID => Approved Address`, where
          \t *      Approved Address is an address allowed transfer ownership for the token
          \t *      defined by Token ID
          \t */
          \tmapping(uint256 => address) internal approvals;
          \t/**
          \t * @notice Addresses approved by token owners to transfer all their tokens
          \t *
          \t * @dev Maps `Token Owner Address => Operator Address => Approval State` - true/false (approved/not), where
          \t *      - Token Owner Address is any address which may own tokens or not,
          \t *      - Operator Address is any other address which may own tokens or not,
          \t *      - Approval State is a flag indicating if Operator Address is allowed to
          \t *        transfer tokens owned by Token Owner Address o their behalf
          \t */
          \tmapping(address => mapping(address => bool)) internal approvedOperators;
          \t/**
          \t * @dev A record of nonces for signing/validating signatures in EIP-712 based
          \t *      `permit` and `permitForAll` functions
          \t *
          \t * @dev Each time the nonce is used, it is increased by one, meaning reordering
          \t *      of the EIP-712 transactions is not possible
          \t *
          \t * @dev Inspired by EIP-2612 extension for ERC20 token standard
          \t *
          \t * @dev Maps token owner address => token owner nonce
          \t */
          \tmapping(address => uint256) public permitNonces;
          \t/**
          \t * @dev Base URI is used to construct ERC721Metadata.tokenURI as
          \t *      `base URI + token ID` if token URI is not set (not present in `_tokenURIs` mapping)
          \t *
          \t * @dev For example, if base URI is https://api.com/token/, then token #1
          \t *      will have an URI https://api.com/token/1
          \t *
          \t * @dev If token URI is set with `setTokenURI()` it will be returned as is via `tokenURI()`
          \t */
          \tstring public override baseURI = "";
          \t/**
          \t * @dev Optional mapping for token URIs to be returned as is when `tokenURI()`
          \t *      is called; if mapping doesn't exist for token, the URI is constructed
          \t *      as `base URI + token ID`, where plus (+) denotes string concatenation
          \t */
          \tmapping(uint256 => string) internal _tokenURIs;
          \t/**
          \t * @dev 32 bit token ID space is optimal for batch minting in batches of size 8
          \t *      8 * 32 = 256 - single storage slot in global/local collection(s)
          \t */
          \tuint8 public constant BATCH_SIZE_MULTIPLIER = 8;
          \t/**
          \t * @notice Enables ERC721 transfers of the tokens
          \t *      (transfer by the token owner himself)
          \t * @dev Feature FEATURE_TRANSFERS must be enabled in order for
          \t *      `transferFrom()` function to succeed when executed by token owner
          \t */
          \tuint32 public constant FEATURE_TRANSFERS = 0x0000_0001;
          \t/**
          \t * @notice Enables ERC721 transfers on behalf
          \t *      (transfer by someone else on behalf of token owner)
          \t * @dev Feature FEATURE_TRANSFERS_ON_BEHALF must be enabled in order for
          \t *      `transferFrom()` function to succeed whe executed by approved operator
          \t * @dev Token owner must call `approve()` or `setApprovalForAll()`
          \t *      first to authorize the transfer on behalf
          \t */
          \tuint32 public constant FEATURE_TRANSFERS_ON_BEHALF = 0x0000_0002;
          \t/**
          \t * @notice Enables token owners to burn their own tokens
          \t *
          \t * @dev Feature FEATURE_OWN_BURNS must be enabled in order for
          \t *      `burn()` function to succeed when called by token owner
          \t */
          \tuint32 public constant FEATURE_OWN_BURNS = 0x0000_0008;
          \t/**
          \t * @notice Enables approved operators to burn tokens on behalf of their owners
          \t *
          \t * @dev Feature FEATURE_BURNS_ON_BEHALF must be enabled in order for
          \t *      `burn()` function to succeed when called by approved operator
          \t */
          \tuint32 public constant FEATURE_BURNS_ON_BEHALF = 0x0000_0010;
          \t/**
          \t * @notice Enables approvals on behalf (permits via an EIP712 signature)
          \t * @dev Feature FEATURE_PERMITS must be enabled in order for
          \t *      `permit()` function to succeed
          \t */
          \tuint32 public constant FEATURE_PERMITS = 0x0000_0200;
          \t/**
          \t * @notice Enables operator approvals on behalf (permits for all via an EIP712 signature)
          \t * @dev Feature FEATURE_OPERATOR_PERMITS must be enabled in order for
          \t *      `permitForAll()` function to succeed
          \t */
          \tuint32 public constant FEATURE_OPERATOR_PERMITS = 0x0000_0400;
          \t/**
          \t * @notice Token creator is responsible for creating (minting)
          \t *      tokens to an arbitrary address
          \t * @dev Role ROLE_TOKEN_CREATOR allows minting tokens
          \t *      (calling `mint` function)
          \t */
          \tuint32 public constant ROLE_TOKEN_CREATOR = 0x0001_0000;
          \t/**
          \t * @notice Token destroyer is responsible for destroying (burning)
          \t *      tokens owned by an arbitrary address
          \t * @dev Role ROLE_TOKEN_DESTROYER allows burning tokens
          \t *      (calling `burn` function)
          \t */
          \tuint32 public constant ROLE_TOKEN_DESTROYER = 0x0002_0000;
          \t/**
          \t * @notice URI manager is responsible for managing base URI
          \t *      part of the token URI ERC721Metadata interface
          \t *
          \t * @dev Role ROLE_URI_MANAGER allows updating the base URI
          \t *      (executing `setBaseURI` function)
          \t */
          \tuint32 public constant ROLE_URI_MANAGER = 0x0010_0000;
          \t/**
          \t * @notice EIP-712 contract's domain typeHash,
          \t *      see https://eips.ethereum.org/EIPS/eip-712#rationale-for-typehash
          \t *
          \t * @dev Note: we do not include version into the domain typehash/separator,
          \t *      it is implied version is concatenated to the name field, like "AliERC721v1"
          \t */
          \t// keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)")
          \tbytes32 public constant DOMAIN_TYPEHASH = 0x8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866;
          \t/**
          \t * @notice EIP-712 contract's domain separator,
          \t *      see https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator
          \t */
          \tbytes32 public immutable DOMAIN_SEPARATOR;
          \t/**
          \t * @notice EIP-712 permit (EIP-2612) struct typeHash,
          \t *      see https://eips.ethereum.org/EIPS/eip-712#rationale-for-typehash
          \t */
          \t// keccak256("Permit(address owner,address operator,uint256 tokenId,uint256 nonce,uint256 deadline)")
          \tbytes32 public constant PERMIT_TYPEHASH = 0xee2282d7affd5a432b221a559e429129347b0c19a3f102179a5fb1859eef3d29;
          \t/**
          \t * @notice EIP-712 permitForAll (EIP-2612) struct typeHash,
          \t *      see https://eips.ethereum.org/EIPS/eip-712#rationale-for-typehash
          \t */
          \t// keccak256("PermitForAll(address owner,address operator,bool approved,uint256 nonce,uint256 deadline)")
          \tbytes32 public constant PERMIT_FOR_ALL_TYPEHASH = 0x47ab88482c90e4bb94b82a947ae78fa91fb25de1469ab491f4c15b9a0a2677ee;
          \t/**
          \t * @dev Fired in setBaseURI()
          \t *
          \t * @param _by an address which executed update
          \t * @param _oldVal old _baseURI value
          \t * @param _newVal new _baseURI value
          \t */
          \tevent BaseURIUpdated(address indexed _by, string _oldVal, string _newVal);
          \t/**
          \t * @dev Fired in setTokenURI()
          \t *
          \t * @param _by an address which executed update
          \t * @param _tokenId token ID which URI was updated
          \t * @param _oldVal old _baseURI value
          \t * @param _newVal new _baseURI value
          \t */
          \tevent TokenURIUpdated(address indexed _by, uint256 _tokenId, string _oldVal, string _newVal);
          \t/**
          \t * @dev Constructs/deploys ERC721 instance with the name and symbol specified
          \t *
          \t * @param _name name of the token to be accessible as `name()`,
          \t *      ERC-20 compatible descriptive name for a collection of NFTs in this contract
          \t * @param _symbol token symbol to be accessible as `symbol()`,
          \t *      ERC-20 compatible descriptive name for a collection of NFTs in this contract
          \t */
          \tconstructor(string memory _name, string memory _symbol) {
          \t\t// set the name
          \t\tname = _name;
          \t\t// set the symbol
          \t\tsymbol = _symbol;
          \t\t// build the EIP-712 contract domain separator, see https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator
          \t\t// note: we specify contract version in its name
          \t\tDOMAIN_SEPARATOR = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes("AliERC721v1")), block.chainid, address(this)));
          \t}
          \t/**
          \t * @dev Verifies if token is transferable (i.e. can change ownership, allowed to be transferred);
          \t *      The default behaviour is to always allow transfer if token exists
          \t *
          \t * @dev Implementations may modify the default behaviour based on token metadata
          \t *      if required
          \t *
          \t * @param _tokenId ID of the token to check if it's transferable
          \t * @return true if token is transferable, false otherwise
          \t */
          \tfunction isTransferable(uint256 _tokenId) public view virtual returns(bool) {
          \t\t// validate token existence
          \t\trequire(exists(_tokenId), "token doesn't exist");
          \t\t// generic implementation returns true if token exists
          \t\treturn true;
          \t}
          \t/**
          \t * @notice Checks if specified token exists
          \t *
          \t * @dev Returns whether the specified token ID has an ownership
          \t *      information associated with it
          \t *
          \t * @inheritdoc MintableERC721
          \t *
          \t * @param _tokenId ID of the token to query existence for
          \t * @return whether the token exists (true - exists, false - doesn't exist)
          \t */
          \tfunction exists(uint256 _tokenId) public override view returns(bool) {
          \t\t// read ownership information and return a check if it's not zero (set)
          \t\treturn tokens[_tokenId] != 0;
          \t}
          \t/**
          \t * @inheritdoc ERC165
          \t */
          \tfunction supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
          \t\t// construct the interface support from required and optional ERC721 interfaces
          \t\treturn interfaceId == type(ERC165).interfaceId
          \t\t\t|| interfaceId == type(ERC721).interfaceId
          \t\t\t|| interfaceId == type(ERC721Metadata).interfaceId
          \t\t\t|| interfaceId == type(ERC721Enumerable).interfaceId
          \t\t\t|| interfaceId == type(MintableERC721).interfaceId
          \t\t\t|| interfaceId == type(BurnableERC721).interfaceId;
          \t}
          \t// ===== Start: ERC721 Metadata =====
          \t/**
          \t * @dev Restricted access function which updates base URI used to construct
          \t *      ERC721Metadata.tokenURI
          \t *
          \t * @dev Requires executor to have ROLE_URI_MANAGER permission
          \t *
          \t * @param _baseURI new base URI to set
          \t */
          \tfunction setBaseURI(string memory _baseURI) public virtual {
          \t\t// verify the access permission
          \t\trequire(isSenderInRole(ROLE_URI_MANAGER), "access denied");
          \t\t// emit an event first - to log both old and new values
          \t\temit BaseURIUpdated(msg.sender, baseURI, _baseURI);
          \t\t// and update base URI
          \t\tbaseURI = _baseURI;
          \t}
          \t/**
          \t * @dev Returns token URI if it was previously set with `setTokenURI`,
          \t *      otherwise constructs it as base URI + token ID
          \t *
          \t * @inheritdoc ERC721Metadata
          \t */
          \tfunction tokenURI(uint256 _tokenId) public view override returns (string memory) {
          \t\t// verify token exists
          \t\trequire(exists(_tokenId), "token doesn't exist");
          \t\t// read the token URI for the token specified
          \t\tstring memory _tokenURI = _tokenURIs[_tokenId];
          \t\t// if token URI is set
          \t\tif(bytes(_tokenURI).length > 0) {
          \t\t\t// just return it
          \t\t\treturn _tokenURI;
          \t\t}
          \t\t// if base URI is not set
          \t\tif(bytes(baseURI).length == 0) {
          \t\t\t// return an empty string
          \t\t\treturn "";
          \t\t}
          \t\t// otherwise concatenate base URI + token ID
          \t\treturn StringUtils.concat(baseURI, StringUtils.itoa(_tokenId, 10));
          \t}
          \t/**
          \t * @dev Sets the token URI for the token defined by its ID
          \t *
          \t * @param _tokenId an ID of the token to set URI for
          \t * @param _tokenURI token URI to set
          \t */
          \tfunction setTokenURI(uint256 _tokenId, string memory _tokenURI) public virtual {
          \t\t// verify the access permission
          \t\trequire(isSenderInRole(ROLE_URI_MANAGER), "access denied");
          \t\t// we do not verify token existence: we want to be able to
          \t\t// preallocate token URIs before tokens are actually minted
          \t\t// emit an event first - to log both old and new values
          \t\temit TokenURIUpdated(msg.sender, _tokenId, _tokenURIs[_tokenId], _tokenURI);
          \t\t// and update token URI
          \t\t_tokenURIs[_tokenId] = _tokenURI;
          \t}
          \t// ===== End: ERC721 Metadata =====
          \t// ===== Start: ERC721, ERC721Enumerable Getters (view functions) =====
          \t/**
          \t * @inheritdoc ERC721
          \t */
          \tfunction balanceOf(address _owner) public view override returns (uint256) {
          \t\t// check `_owner` address is set
          \t\trequire(_owner != address(0), "zero address");
          \t\t// derive owner balance for the their owned tokens collection
          \t\t// as the length of that collection
          \t\treturn collections[_owner].length;
          \t}
          \t/**
          \t * @inheritdoc ERC721
          \t */
          \tfunction ownerOf(uint256 _tokenId) public view override returns (address) {
          \t\t// derive ownership information of the token from the ownership mapping
          \t\t// by extracting lower 160 bits of the mapping value as an address
          \t\taddress owner = address(uint160(tokens[_tokenId]));
          \t\t// verify owner/token exists
          \t\trequire(owner != address(0), "token doesn't exist");
          \t\t// return owner address
          \t\treturn owner;
          \t}
          \t/**
          \t * @inheritdoc ERC721Enumerable
          \t */
          \tfunction totalSupply() public view override returns (uint256) {
          \t\t// derive total supply value from the array of all existing tokens
          \t\t// as the length of this array
          \t\treturn allTokens.length;
          \t}
          \t/**
          \t * @inheritdoc ERC721Enumerable
          \t */
          \tfunction tokenByIndex(uint256 _index) public view override returns (uint256) {
          \t\t// index out of bounds check
          \t\trequire(_index < totalSupply(), "index out of bounds");
          \t\t// find the token ID requested and return
          \t\treturn allTokens[_index];
          \t}
          \t/**
          \t * @inheritdoc ERC721Enumerable
          \t */
          \tfunction tokenOfOwnerByIndex(address _owner, uint256 _index) public view override returns (uint256) {
          \t\t// index out of bounds check
          \t\trequire(_index < balanceOf(_owner), "index out of bounds");
          \t\t// find the token ID requested and return
          \t\treturn collections[_owner][_index];
          \t}
          \t/**
          \t * @inheritdoc ERC721
          \t */
          \tfunction getApproved(uint256 _tokenId) public view override returns (address) {
          \t\t// verify token specified exists
          \t\trequire(exists(_tokenId), "token doesn't exist");
          \t\t// read the approval value and return
          \t\treturn approvals[_tokenId];
          \t}
          \t/**
          \t * @inheritdoc ERC721
          \t */
          \tfunction isApprovedForAll(address _owner, address _operator) public view override returns (bool) {
          \t\t// read the approval state value and return
          \t\treturn approvedOperators[_owner][_operator];
          \t}
          \t// ===== End: ERC721, ERC721Enumerable Getters (view functions) =====
          \t// ===== Start: ERC721 mutative functions (transfers, approvals) =====
          \t/**
          \t * @inheritdoc ERC721
          \t */
          \tfunction safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory _data) public override {
          \t\t// delegate call to unsafe transfer on behalf `transferFrom()`
          \t\ttransferFrom(_from, _to, _tokenId);
          \t\t// if receiver `_to` is a smart contract
          \t\tif(AddressUtils.isContract(_to)) {
          \t\t\t// check it supports ERC721 interface - execute onERC721Received()
          \t\t\tbytes4 response = ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data);
          \t\t\t// expected response is ERC721TokenReceiver(_to).onERC721Received.selector
          \t\t\t// bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))
          \t\t\trequire(response == ERC721TokenReceiver(_to).onERC721Received.selector, "invalid onERC721Received response");
          \t\t}
          \t}
          \t/**
          \t * @inheritdoc ERC721
          \t */
          \tfunction safeTransferFrom(address _from, address _to, uint256 _tokenId) public override {
          \t\t// delegate call to overloaded `safeTransferFrom()`, set data to ""
          \t\tsafeTransferFrom(_from, _to, _tokenId, "");
          \t}
          \t/**
          \t * @inheritdoc ERC721
          \t */
          \tfunction transferFrom(address _from, address _to, uint256 _tokenId) public override {
          \t\t// if `_from` is equal to sender, require transfers feature to be enabled
          \t\t// otherwise require transfers on behalf feature to be enabled
          \t\trequire(_from == msg.sender && isFeatureEnabled(FEATURE_TRANSFERS)
          \t\t     || _from != msg.sender && isFeatureEnabled(FEATURE_TRANSFERS_ON_BEHALF),
          \t\t        _from == msg.sender? "transfers are disabled": "transfers on behalf are disabled");
          \t\t// validate destination address is set
          \t\trequire(_to != address(0), "zero address");
          \t\t// validate token ownership, which also
          \t\t// validates token existence under the hood
          \t\trequire(_from == ownerOf(_tokenId), "access denied");
          \t\t// verify operator (transaction sender) is either token owner,
          \t\t// or is approved by the token owner to transfer this particular token,
          \t\t// or is approved by the token owner to transfer any of his tokens
          \t\trequire(_from == msg.sender || msg.sender == getApproved(_tokenId) || isApprovedForAll(_from, msg.sender), "access denied");
          \t\t// transfer is not allowed for a locked token
          \t\trequire(isTransferable(_tokenId), "locked token");
          \t\t// if required, move token ownership,
          \t\t// update old and new owner's token collections accordingly:
          \t\tif(_from != _to) {
          \t\t\t// remove token from old owner's collection (also clears approval)
          \t\t\t__removeLocal(_tokenId);
          \t\t\t// add token to the new owner's collection
          \t\t\t__addLocal(_tokenId, _to);
          \t\t}
          \t\t// even if no real changes are required, approval needs to be erased
          \t\telse {
          \t\t\t// clear token approval (also emits an Approval event)
          \t\t\t__clearApproval(_from, _tokenId);
          \t\t}
          \t\t// fire ERC721 transfer event
          \t\temit Transfer(_from, _to, _tokenId);
          \t}
          \t/**
          \t * @inheritdoc ERC721
          \t */
          \tfunction approve(address _approved, uint256 _tokenId) public override {
          \t\t// make an internal approve - delegate to `__approve`
          \t\t__approve(msg.sender, _approved, _tokenId);
          \t}
          \t/**
          \t * @dev Powers the meta transaction for `approve` - EIP-712 signed `permit`
          \t *
          \t * @dev Approves address called `_operator` to transfer token `_tokenId`
          \t *      on behalf of the `_owner`
          \t *
          \t * @dev Zero `_operator` address indicates there is no approved address,
          \t *      and effectively removes an approval for the token specified
          \t *
          \t * @dev `_owner` must own token `_tokenId` to grant the permission
          \t * @dev Throws if `_operator` is a self address (`_owner`),
          \t *      or if `_tokenId` doesn't exist
          \t *
          \t * @param _owner owner of the token `_tokenId` to set approval on behalf of
          \t * @param _operator an address approved by the token owner
          \t *      to spend token `_tokenId` on its behalf
          \t * @param _tokenId token ID operator `_approved` is allowed to
          \t *      transfer on behalf of the token owner
          \t */
          \tfunction __approve(address _owner, address _operator, uint256 _tokenId) private {
          \t\t// get token owner address
          \t\taddress owner = ownerOf(_tokenId);
          \t\t// approving owner address itself doesn't make sense and is not allowed
          \t\trequire(_operator != owner, "self approval");
          \t\t// only token owner or/and approved operator can set the approval
          \t\trequire(_owner == owner || isApprovedForAll(owner, _owner), "access denied");
          \t\t// update the approval
          \t\tapprovals[_tokenId] = _operator;
          \t\t// emit an event
          \t\temit Approval(owner, _operator, _tokenId);
          \t}
          \t/**
          \t * @inheritdoc ERC721
          \t */
          \tfunction setApprovalForAll(address _operator, bool _approved) public override {
          \t\t// make an internal approve - delegate to `__approveForAll`
          \t\t__approveForAll(msg.sender, _operator, _approved);
          \t}
          \t/**
          \t * @dev Powers the meta transaction for `setApprovalForAll` - EIP-712 signed `permitForAll`
          \t *
          \t * @dev Approves address called `_operator` to transfer any tokens
          \t *      on behalf of the `_owner`
          \t *
          \t * @dev `_owner` must not necessarily own any tokens to grant the permission
          \t * @dev Throws if `_operator` is a self address (`_owner`)
          \t *
          \t * @param _owner owner of the tokens to set approval on behalf of
          \t * @param _operator an address to add to the set of authorized operators, i.e.
          \t *      an address approved by the token owner to spend tokens on its behalf
          \t * @param _approved true if the operator is approved, false to revoke approval
          \t */
          \tfunction __approveForAll(address _owner, address _operator, bool _approved) private {
          \t\t// approving tx sender address itself doesn't make sense and is not allowed
          \t\trequire(_operator != _owner, "self approval");
          \t\t// update the approval
          \t\tapprovedOperators[_owner][_operator] = _approved;
          \t\t// emit an event
          \t\temit ApprovalForAll(_owner, _operator, _approved);
          \t}
          \t/**
          \t * @dev Clears approval for a given token owned by a given owner,
          \t *      emits an Approval event
          \t *
          \t * @dev Unsafe: doesn't check the validity of inputs (must be kept private),
          \t *      assuming the check is done by the caller
          \t *      - token existence
          \t *      - token ownership
          \t *
          \t * @param _owner token owner to be logged into Approved event as is
          \t * @param _tokenId token ID to erase approval for and to log into Approved event as is
          \t */
          \tfunction __clearApproval(address _owner, uint256 _tokenId) internal {
          \t\t// clear token approval
          \t\tdelete approvals[_tokenId];
          \t\t// emit an ERC721 Approval event:
          \t\t// "When a Transfer event emits, this also indicates that the approved
          \t\t// address for that NFT (if any) is reset to none."
          \t\temit Approval(_owner, address(0), _tokenId);
          \t}
          \t// ===== End: ERC721 mutative functions (transfers, approvals) =====
          \t// ===== Start: Meta-transactions Support =====
          \t/**
          \t * @notice Change or reaffirm the approved address for an NFT on behalf
          \t *
          \t * @dev Executes approve(_operator, _tokenId) on behalf of the token owner
          \t *      who EIP-712 signed the transaction, i.e. as if transaction sender is the EIP712 signer
          \t *
          \t * @dev Sets the `_tokenId` as the allowance of `_operator` over `_owner` token,
          \t *      given `_owner` EIP-712 signed approval
          \t *
          \t * @dev Emits `Approval` event in the same way as `approve` does
          \t *
          \t * @dev Requires:
          \t *     - `_operator` to be non-zero address
          \t *     - `_exp` to be a timestamp in the future
          \t *     - `v`, `r` and `s` to be a valid `secp256k1` signature from `_owner`
          \t *        over the EIP712-formatted function arguments.
          \t *     - the signature to use `_owner` current nonce (see `permitNonces`).
          \t *
          \t * @dev For more information on the signature format, see the
          \t *      https://eips.ethereum.org/EIPS/eip-2612#specification
          \t *
          \t * @param _owner owner of the token to set approval on behalf of,
          \t *      an address which signed the EIP-712 message
          \t * @param _operator new approved NFT controller
          \t * @param _tokenId token ID to approve
          \t * @param _exp signature expiration time (unix timestamp)
          \t * @param v the recovery byte of the signature
          \t * @param r half of the ECDSA signature pair
          \t * @param s half of the ECDSA signature pair
          \t */
          \tfunction permit(address _owner, address _operator, uint256 _tokenId, uint256 _exp, uint8 v, bytes32 r, bytes32 s) public {
          \t\t// verify permits are enabled
          \t\trequire(isFeatureEnabled(FEATURE_PERMITS), "permits are disabled");
          \t\t// derive signer of the EIP712 Permit message, and
          \t\t// update the nonce for that particular signer to avoid replay attack!!! ----------->>> ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
          \t\taddress signer = __deriveSigner(abi.encode(PERMIT_TYPEHASH, _owner, _operator, _tokenId, permitNonces[_owner]++, _exp), v, r, s);
          \t\t// perform message integrity and security validations
          \t\trequire(signer == _owner, "invalid signature");
          \t\trequire(block.timestamp < _exp, "signature expired");
          \t\t// delegate call to `__approve` - execute the logic required
          \t\t__approve(_owner, _operator, _tokenId);
          \t}
          \t/**
          \t * @notice Enable or disable approval for a third party ("operator") to manage
          \t *      all of owner's assets - on behalf
          \t *
          \t * @dev Executes setApprovalForAll(_operator, _approved) on behalf of the owner
          \t *      who EIP-712 signed the transaction, i.e. as if transaction sender is the EIP712 signer
          \t *
          \t * @dev Sets the `_operator` as the token operator for `_owner` tokens,
          \t *      given `_owner` EIP-712 signed approval
          \t *
          \t * @dev Emits `ApprovalForAll` event in the same way as `setApprovalForAll` does
          \t *
          \t * @dev Requires:
          \t *     - `_operator` to be non-zero address
          \t *     - `_exp` to be a timestamp in the future
          \t *     - `v`, `r` and `s` to be a valid `secp256k1` signature from `_owner`
          \t *        over the EIP712-formatted function arguments.
          \t *     - the signature to use `_owner` current nonce (see `permitNonces`).
          \t *
          \t * @dev For more information on the signature format, see the
          \t *      https://eips.ethereum.org/EIPS/eip-2612#specification
          \t *
          \t * @param _owner owner of the tokens to set approval on behalf of,
          \t *      an address which signed the EIP-712 message
          \t * @param _operator an address to add to the set of authorized operators, i.e.
          \t *      an address approved by the token owner to spend tokens on its behalf
          \t * @param _approved true if the operator is approved, false to revoke approval
          \t * @param _exp signature expiration time (unix timestamp)
          \t * @param v the recovery byte of the signature
          \t * @param r half of the ECDSA signature pair
          \t * @param s half of the ECDSA signature pair
          \t */
          \tfunction permitForAll(address _owner, address _operator, bool _approved, uint256 _exp, uint8 v, bytes32 r, bytes32 s) public {
          \t\t// verify permits are enabled
          \t\trequire(isFeatureEnabled(FEATURE_OPERATOR_PERMITS), "operator permits are disabled");
          \t\t// derive signer of the EIP712 PermitForAll message, and
          \t\t// update the nonce for that particular signer to avoid replay attack!!! --------------------->>> ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
          \t\taddress signer = __deriveSigner(abi.encode(PERMIT_FOR_ALL_TYPEHASH, _owner, _operator, _approved, permitNonces[_owner]++, _exp), v, r, s);
          \t\t// perform message integrity and security validations
          \t\trequire(signer == _owner, "invalid signature");
          \t\trequire(block.timestamp < _exp, "signature expired");
          \t\t// delegate call to `__approve` - execute the logic required
          \t\t__approveForAll(_owner, _operator, _approved);
          \t}
          \t/**
          \t * @dev Auxiliary function to verify structured EIP712 message signature and derive its signer
          \t *
          \t * @param abiEncodedTypehash abi.encode of the message typehash together with all its parameters
          \t * @param v the recovery byte of the signature
          \t * @param r half of the ECDSA signature pair
          \t * @param s half of the ECDSA signature pair
          \t */
          \tfunction __deriveSigner(bytes memory abiEncodedTypehash, uint8 v, bytes32 r, bytes32 s) private view returns(address) {
          \t\t// build the EIP-712 hashStruct of the message
          \t\tbytes32 hashStruct = keccak256(abiEncodedTypehash);
          \t\t// calculate the EIP-712 digest "\\x19\\x01" ‖ domainSeparator ‖ hashStruct(message)
          \t\tbytes32 digest = keccak256(abi.encodePacked("\\x19\\x01", DOMAIN_SEPARATOR, hashStruct));
          \t\t// recover the address which signed the message with v, r, s
          \t\taddress signer = ECDSA.recover(digest, v, r, s);
          \t\t// return the signer address derived from the signature
          \t\treturn signer;
          \t}
          \t// ===== End: Meta-transactions Support =====
          \t// ===== Start: mint/burn support =====
          \t/**
          \t * @dev Creates new token with token ID specified
          \t *      and assigns an ownership `_to` for this token
          \t *
          \t * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls
          \t *      `onERC721Received` on `_to` and throws if the return value is not
          \t *      `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
          \t *
          \t * @dev Requires executor to have `ROLE_TOKEN_CREATOR` permission
          \t *
          \t * @param _to an address to mint token to
          \t * @param _tokenId ID of the token to mint
          \t * @param _data additional data with no specified format, sent in call to `_to`
          \t */
          \tfunction safeMint(address _to, uint256 _tokenId, bytes memory _data) public override {
          \t\t// delegate to unsafe mint
          \t\tmint(_to, _tokenId);
          \t\t// make it safe: execute `onERC721Received`
          \t\t// if receiver `_to` is a smart contract
          \t\tif(AddressUtils.isContract(_to)) {
          \t\t\t// check it supports ERC721 interface - execute onERC721Received()
          \t\t\tbytes4 response = ERC721TokenReceiver(_to).onERC721Received(msg.sender, address(0), _tokenId, _data);
          \t\t\t// expected response is ERC721TokenReceiver(_to).onERC721Received.selector
          \t\t\t// bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))
          \t\t\trequire(response == ERC721TokenReceiver(_to).onERC721Received.selector, "invalid onERC721Received response");
          \t\t}
          \t}
          \t/**
          \t * @dev Creates new token with token ID specified
          \t *      and assigns an ownership `_to` for this token
          \t *
          \t * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls
          \t *      `onERC721Received` on `_to` and throws if the return value is not
          \t *      `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
          \t *
          \t * @dev Requires executor to have `ROLE_TOKEN_CREATOR` permission
          \t *
          \t * @param _to an address to mint token to
          \t * @param _tokenId ID of the token to mint
          \t */
          \tfunction safeMint(address _to, uint256 _tokenId) public override {
          \t\t// delegate to `safeMint` with empty data
          \t\tsafeMint(_to, _tokenId, "");
          \t}
          \t/**
          \t * @dev Creates new tokens starting with token ID specified
          \t *      and assigns an ownership `_to` for these tokens
          \t *
          \t * @dev Token IDs to be minted: [_tokenId, _tokenId + n)
          \t *
          \t * @dev n must be greater or equal 2: `n > 1`
          \t *
          \t * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls
          \t *      `onERC721Received` on `_to` and throws if the return value is not
          \t *      `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
          \t *
          \t * @dev Requires executor to have `ROLE_TOKEN_CREATOR` permission
          \t *
          \t * @param _to an address to mint token to
          \t * @param _tokenId ID of the token to mint
          \t * @param n how many tokens to mint, sequentially increasing the _tokenId
          \t * @param _data additional data with no specified format, sent in call to `_to`
          \t */
          \tfunction safeMintBatch(address _to, uint256 _tokenId, uint256 n, bytes memory _data) public override {
          \t\t// delegate to unsafe mint
          \t\tmintBatch(_to, _tokenId, n);
          \t\t// make it safe: execute `onERC721Received`
          \t\t// if receiver `_to` is a smart contract
          \t\tif(AddressUtils.isContract(_to)) {
          \t\t\t// onERC721Received: for each token minted
          \t\t\tfor(uint256 i = 0; i < n; i++) {
          \t\t\t\t// check it supports ERC721 interface - execute onERC721Received()
          \t\t\t\tbytes4 response = ERC721TokenReceiver(_to).onERC721Received(msg.sender, address(0), _tokenId + i, _data);
          \t\t\t\t// expected response is ERC721TokenReceiver(_to).onERC721Received.selector
          \t\t\t\t// bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))
          \t\t\t\trequire(response == ERC721TokenReceiver(_to).onERC721Received.selector, "invalid onERC721Received response");
          \t\t\t}
          \t\t}
          \t}
          \t/**
          \t * @dev Creates new tokens starting with token ID specified
          \t *      and assigns an ownership `_to` for these tokens
          \t *
          \t * @dev Token IDs to be minted: [_tokenId, _tokenId + n)
          \t *
          \t * @dev n must be greater or equal 2: `n > 1`
          \t *
          \t * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls
          \t *      `onERC721Received` on `_to` and throws if the return value is not
          \t *      `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
          \t *
          \t * @dev Requires executor to have `ROLE_TOKEN_CREATOR` permission
          \t *
          \t * @param _to an address to mint token to
          \t * @param _tokenId ID of the token to mint
          \t * @param n how many tokens to mint, sequentially increasing the _tokenId
          \t */
          \tfunction safeMintBatch(address _to, uint256 _tokenId, uint256 n) public override {
          \t\t// delegate to `safeMint` with empty data
          \t\tsafeMintBatch(_to, _tokenId, n, "");
          \t}
          \t/**
          \t * @dev Creates new token with token ID specified
          \t *      and assigns an ownership `_to` for this token
          \t *
          \t * @dev Unsafe: doesn't execute `onERC721Received` on the receiver.
          \t *      Prefer the use of `saveMint` instead of `mint`.
          \t *
          \t * @dev Requires executor to have `ROLE_TOKEN_CREATOR` permission
          \t *
          \t * @param _to an address to mint token to
          \t * @param _tokenId ID of the token to mint
          \t */
          \tfunction mint(address _to, uint256 _tokenId) public override {
          \t\t// check if caller has sufficient permissions to mint tokens
          \t\trequire(isSenderInRole(ROLE_TOKEN_CREATOR), "access denied");
          \t\t// verify the inputs
          \t\t// verify destination address is set
          \t\trequire(_to != address(0), "zero address");
          \t\t// verify the token ID is "tiny" (32 bits long at most)
          \t\trequire(uint32(_tokenId) == _tokenId, "token ID overflow");
          \t\t// verify token doesn't yet exist
          \t\trequire(!exists(_tokenId), "already minted");
          \t\t// create token ownership record,
          \t\t// add token to `allTokens` and new owner's collections
          \t\t// add token to both local and global collections (enumerations)
          \t\t__addToken(_tokenId, _to);
          \t\t// fire ERC721 transfer event
          \t\temit Transfer(address(0), _to, _tokenId);
          \t}
          \t/**
          \t * @dev Creates new tokens starting with token ID specified
          \t *      and assigns an ownership `_to` for these tokens
          \t *
          \t * @dev Token IDs to be minted: [_tokenId, _tokenId + n)
          \t *
          \t * @dev n must be greater or equal 2: `n > 1`
          \t *
          \t * @dev Unsafe: doesn't execute `onERC721Received` on the receiver.
          \t *      Prefer the use of `saveMintBatch` instead of `mintBatch`.
          \t *
          \t * @dev Requires executor to have `ROLE_TOKEN_CREATOR` permission
          \t *
          \t * @param _to an address to mint tokens to
          \t * @param _tokenId ID of the first token to mint
          \t * @param n how many tokens to mint, sequentially increasing the _tokenId
          \t */
          \tfunction mintBatch(address _to, uint256 _tokenId, uint256 n) public override {
          \t\t// check if caller has sufficient permissions to mint tokens
          \t\trequire(isSenderInRole(ROLE_TOKEN_CREATOR), "access denied");
          \t\t// verify the inputs
          \t\t// verify destination address is set
          \t\trequire(_to != address(0), "zero address");
          \t\t// verify n is set properly
          \t\trequire(n > 1, "n is too small");
          \t\t// verify the token ID is "tiny" (32 bits long at most)
          \t\trequire(uint32(_tokenId) == _tokenId, "token ID overflow");
          \t\trequire(uint32(_tokenId + n - 1) == _tokenId + n - 1, "n-th token ID overflow");
          \t\t// verification: for each token to be minted
          \t\tfor(uint256 i = 0; i < n; i++) {
          \t\t\t// verify token doesn't yet exist
          \t\t\trequire(!exists(_tokenId + i), "already minted");
          \t\t}
          \t\t// create token ownership records,
          \t\t// add tokens to `allTokens` and new owner's collections
          \t\t// add tokens to both local and global collections (enumerations)
          \t\t__addTokens(_to, _tokenId, n);
          \t\t// events: for each token minted
          \t\tfor(uint256 i = 0; i < n; i++) {
          \t\t\t// fire ERC721 transfer event
          \t\t\temit Transfer(address(0), _to, _tokenId + i);
          \t\t}
          \t}
          \t/**
          \t * @dev Destroys the token with token ID specified
          \t *
          \t * @dev Requires executor to have `ROLE_TOKEN_DESTROYER` permission
          \t *      or FEATURE_OWN_BURNS/FEATURE_BURNS_ON_BEHALF features to be enabled
          \t *
          \t * @dev Can be disabled by the contract creator forever by disabling
          \t *      FEATURE_OWN_BURNS/FEATURE_BURNS_ON_BEHALF features and then revoking
          \t *      its own roles to burn tokens and to enable burning features
          \t *
          \t * @param _tokenId ID of the token to burn
          \t */
          \tfunction burn(uint256 _tokenId) public override {
          \t\t// read token owner data
          \t\t// verifies token exists under the hood
          \t\taddress _from = ownerOf(_tokenId);
          \t\t// check if caller has sufficient permissions to burn tokens
          \t\t// and if not - check for possibility to burn own tokens or to burn on behalf
          \t\tif(!isSenderInRole(ROLE_TOKEN_DESTROYER)) {
          \t\t\t// if `_from` is equal to sender, require own burns feature to be enabled
          \t\t\t// otherwise require burns on behalf feature to be enabled
          \t\t\trequire(_from == msg.sender && isFeatureEnabled(FEATURE_OWN_BURNS)
          \t\t\t     || _from != msg.sender && isFeatureEnabled(FEATURE_BURNS_ON_BEHALF),
          \t\t\t        _from == msg.sender? "burns are disabled": "burns on behalf are disabled");
          \t\t\t// verify sender is either token owner, or approved by the token owner to burn tokens
          \t\t\trequire(_from == msg.sender || msg.sender == getApproved(_tokenId) || isApprovedForAll(_from, msg.sender), "access denied");
          \t\t}
          \t\t// remove token ownership record (also clears approval),
          \t\t// remove token from both local and global collections
          \t\t__removeToken(_tokenId);
          \t\t// delete token URI mapping
          \t\tdelete _tokenURIs[_tokenId];
          \t\t// fire ERC721 transfer event
          \t\temit Transfer(_from, address(0), _tokenId);
          \t}
          \t// ===== End: mint/burn support =====
          \t// ----- Start: auxiliary internal/private functions -----
          \t/**
          \t * @dev Adds token to the new owner's collection (local),
          \t *      used internally to transfer existing tokens, to mint new
          \t *
          \t * @dev Unsafe: doesn't check for data structures consistency
          \t *      (token existence, token ownership, etc.)
          \t *
          \t * @dev Must be kept private at all times. Inheriting smart contracts
          \t *      may be interested in overriding this function.
          \t *
          \t * @param _tokenId token ID to add
          \t * @param _to new owner address to add token to
          \t */
          \tfunction __addLocal(uint256 _tokenId, address _to) internal virtual {
          \t\t// get a reference to the collection where token goes to
          \t\tuint32[] storage destination = collections[_to];
          \t\t// update local index and ownership, do not change global index
          \t\ttokens[_tokenId] = tokens[_tokenId]
          \t\t\t//  |unused |global | local | ownership information (address)      |
          \t\t\t& 0x00000000FFFFFFFF000000000000000000000000000000000000000000000000
          \t\t\t| uint192(destination.length) << 160 | uint160(_to);
          \t\t// push token into the local collection
          \t\tdestination.push(uint32(_tokenId));
          \t}
          \t/**
          \t * @dev Add token to both local and global collections (enumerations),
          \t *      used internally to mint new tokens
          \t *
          \t * @dev Unsafe: doesn't check for data structures consistency
          \t *      (token existence, token ownership, etc.)
          \t *
          \t * @dev Must be kept private at all times. Inheriting smart contracts
          \t *      may be interested in overriding this function.
          \t *
          \t * @param _tokenId token ID to add
          \t * @param _to new owner address to add token to
          \t */
          \tfunction __addToken(uint256 _tokenId, address _to) internal virtual {
          \t\t// get a reference to the collection where token goes to
          \t\tuint32[] storage destination = collections[_to];
          \t\t// update token global and local indexes, ownership
          \t\ttokens[_tokenId] = uint224(allTokens.length) << 192 | uint192(destination.length) << 160 | uint160(_to);
          \t\t// push token into the collection
          \t\tdestination.push(uint32(_tokenId));
          \t\t// push it into the global `allTokens` collection (enumeration)
          \t\tallTokens.push(uint32(_tokenId));
          \t}
          \t/**
          \t * @dev Add tokens to both local and global collections (enumerations),
          \t *      used internally to mint new tokens in batches
          \t *
          \t * @dev Token IDs to be added: [_tokenId, _tokenId + n)
          \t *      n is expected to be greater or equal 2, but this is not checked
          \t *
          \t * @dev Unsafe: doesn't check for data structures consistency
          \t *      (token existence, token ownership, etc.)
          \t *
          \t * @dev Must be kept private at all times. Inheriting smart contracts
          \t *      may be interested in overriding this function.
          \t *
          \t * @param _to new owner address to add token to
          \t * @param _tokenId first token ID to add
          \t * @param n how many tokens to add, sequentially increasing the _tokenId
          \t */
          \tfunction __addTokens(address _to, uint256 _tokenId, uint256 n) internal virtual {
          \t\t// get a reference to the collection where tokens go to
          \t\tuint32[] storage destination = collections[_to];
          \t\t// for each token to be added
          \t\tfor(uint256 i = 0; i < n; i++) {
          \t\t\t// update token global and local indexes, ownership
          \t\t\ttokens[_tokenId + i] = uint224(allTokens.length + i) << 192 | uint192(destination.length + i) << 160 | uint160(_to);
          \t\t}
          \t\t// push tokens into the local collection
          \t\tdestination.push32(uint32(_tokenId), uint32(n));
          \t\t// push tokens into the global `allTokens` collection (enumeration)
          \t\tallTokens.push32(uint32(_tokenId), uint32(n));
          \t}
          \t/**
          \t * @dev Removes token from owner's local collection,
          \t *      used internally to transfer or burn existing tokens
          \t *
          \t * @dev Unsafe: doesn't check for data structures consistency
          \t *      (token existence, token ownership, etc.)
          \t *
          \t * @dev Must be kept private at all times. Inheriting smart contracts
          \t *      may be interested in overriding this function.
          \t *
          \t * @param _tokenId token ID to remove
          \t */
          \tfunction __removeLocal(uint256 _tokenId) internal virtual {
          \t\t// read token data, containing global and local indexes, owner address
          \t\tuint256 token = tokens[_tokenId];
          \t\t// get a reference to the token's owner collection (local)
          \t\tuint32[] storage source = collections[address(uint160(token))];
          \t\t// token index within the collection
          \t\tuint32 i = uint32(token >> 160);
          \t\t// get an ID of the last token in the collection
          \t\tuint32 sourceId = source[source.length - 1];
          \t\t// if the token we're to remove from the collection is not the last one,
          \t\t// we need to move last token in the collection into index `i`
          \t\tif(i != source.length - 1) {
          \t\t\t// we put the last token in the collection to the position released
          \t\t\t// update last token local index to point to proper place in the collection
          \t\t\t// preserve global index and ownership info
          \t\t\ttokens[sourceId] = tokens[sourceId]
          \t\t\t\t//  |unused |global | local | ownership information (address)      |
          \t\t\t\t& 0x00000000FFFFFFFF00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
          \t\t\t\t| uint192(i) << 160;
          \t\t\t// put it into the position `i` within the collection
          \t\t\tsource[i] = sourceId;
          \t\t}
          \t\t// trim the collection by removing last element
          \t\tsource.pop();
          \t\t// clear token approval (also emits an Approval event)
          \t\t__clearApproval(address(uint160(token)), _tokenId);
          \t}
          \t/**
          \t * @dev Removes token from both local and global collections (enumerations),
          \t *      used internally to burn existing tokens
          \t *
          \t * @dev Unsafe: doesn't check for data structures consistency
          \t *      (token existence, token ownership, etc.)
          \t *
          \t * @dev Must be kept private at all times. Inheriting smart contracts
          \t *      may be interested in overriding this function.
          \t *
          \t * @param _tokenId token ID to remove
          \t */
          \tfunction __removeToken(uint256 _tokenId) internal virtual {
          \t\t// remove token from owner's (local) collection first
          \t\t__removeLocal(_tokenId);
          \t\t// token index within the global collection
          \t\tuint32 i = uint32(tokens[_tokenId] >> 192);
          \t\t// delete the token
          \t\tdelete tokens[_tokenId];
          \t\t// get an ID of the last token in the collection
          \t\tuint32 lastId = allTokens[allTokens.length - 1];
          \t\t// if the token we're to remove from the collection is not the last one,
          \t\t// we need to move last token in the collection into index `i`
          \t\tif(i != allTokens.length - 1) {
          \t\t\t// we put the last token in the collection to the position released
          \t\t\t// update last token global index to point to proper place in the collection
          \t\t\t// preserve local index and ownership info
          \t\t\ttokens[lastId] = tokens[lastId]
          \t\t\t\t//  |unused |global | local | ownership information (address)      |
          \t\t\t\t& 0x0000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
          \t\t\t\t| uint224(i) << 192;
          \t\t\t// put it into the position `i` within the collection
          \t\t\tallTokens[i] = lastId;
          \t\t}
          \t\t// trim the collection by removing last element
          \t\tallTokens.pop();
          \t}
          \t// ----- End: auxiliary internal/private functions -----
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.7;
          /**
           * @title ERC-165 Standard Interface Detection
           *
           * @dev Interface of the ERC165 standard, as defined in the
           *       https://eips.ethereum.org/EIPS/eip-165[EIP].
           *
           * @dev Implementers can declare support of contract interfaces,
           *      which can then be queried by others.
           *
           * @author Christian Reitwießner, Nick Johnson, Fabian Vogelsteller, Jordi Baylina, Konrad Feldmeier, William Entriken
           */
          interface ERC165 {
          \t/**
          \t * @notice Query if a contract implements an interface
          \t *
          \t * @dev Interface identification is specified in ERC-165.
          \t *      This function uses less than 30,000 gas.
          \t *
          \t * @param interfaceID The interface identifier, as specified in ERC-165
          \t * @return `true` if the contract implements `interfaceID` and
          \t *      `interfaceID` is not 0xffffffff, `false` otherwise
          \t */
          \tfunction supportsInterface(bytes4 interfaceID) external view returns (bool);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.7;
          import "./ERC165Spec.sol";
          /**
           * @title ERC-721 Non-Fungible Token Standard
           *
           * @notice See https://eips.ethereum.org/EIPS/eip-721
           *
           * @dev Solidity issue #3412: The ERC721 interfaces include explicit mutability guarantees for each function.
           *      Mutability guarantees are, in order weak to strong: payable, implicit nonpayable, view, and pure.
           *      Implementation MUST meet the mutability guarantee in this interface and MAY meet a stronger guarantee.
           *      For example, a payable function in this interface may be implemented as nonpayable
           *      (no state mutability specified) in implementing contract.
           *      It is expected a later Solidity release will allow stricter contract to inherit from this interface,
           *      but current workaround is that we edit this interface to add stricter mutability before inheriting:
           *      we have removed all "payable" modifiers.
           *
           * @dev The ERC-165 identifier for this interface is 0x80ac58cd.
           *
           * @author William Entriken, Dieter Shirley, Jacob Evans, Nastassia Sachs
           */
          interface ERC721 is ERC165 {
          \t/// @dev This emits when ownership of any NFT changes by any mechanism.
          \t///  This event emits when NFTs are created (`from` == 0) and destroyed
          \t///  (`to` == 0). Exception: during contract creation, any number of NFTs
          \t///  may be created and assigned without emitting Transfer. At the time of
          \t///  any transfer, the approved address for that NFT (if any) is reset to none.
          \tevent Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
          \t/// @dev This emits when the approved address for an NFT is changed or
          \t///  reaffirmed. The zero address indicates there is no approved address.
          \t///  When a Transfer event emits, this also indicates that the approved
          \t///  address for that NFT (if any) is reset to none.
          \tevent Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
          \t/// @dev This emits when an operator is enabled or disabled for an owner.
          \t///  The operator can manage all NFTs of the owner.
          \tevent ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
          \t/// @notice Count all NFTs assigned to an owner
          \t/// @dev NFTs assigned to the zero address are considered invalid, and this
          \t///  function throws for queries about the zero address.
          \t/// @param _owner An address for whom to query the balance
          \t/// @return The number of NFTs owned by `_owner`, possibly zero
          \tfunction balanceOf(address _owner) external view returns (uint256);
          \t/// @notice Find the owner of an NFT
          \t/// @dev NFTs assigned to zero address are considered invalid, and queries
          \t///  about them do throw.
          \t/// @param _tokenId The identifier for an NFT
          \t/// @return The address of the owner of the NFT
          \tfunction ownerOf(uint256 _tokenId) external view returns (address);
          \t/// @notice Transfers the ownership of an NFT from one address to another address
          \t/// @dev Throws unless `msg.sender` is the current owner, an authorized
          \t///  operator, or the approved address for this NFT. Throws if `_from` is
          \t///  not the current owner. Throws if `_to` is the zero address. Throws if
          \t///  `_tokenId` is not a valid NFT. When transfer is complete, this function
          \t///  checks if `_to` is a smart contract (code size > 0). If so, it calls
          \t///  `onERC721Received` on `_to` and throws if the return value is not
          \t///  `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
          \t/// @param _from The current owner of the NFT
          \t/// @param _to The new owner
          \t/// @param _tokenId The NFT to transfer
          \t/// @param _data Additional data with no specified format, sent in call to `_to`
          \tfunction safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external /*payable*/;
          \t/// @notice Transfers the ownership of an NFT from one address to another address
          \t/// @dev This works identically to the other function with an extra data parameter,
          \t///  except this function just sets data to "".
          \t/// @param _from The current owner of the NFT
          \t/// @param _to The new owner
          \t/// @param _tokenId The NFT to transfer
          \tfunction safeTransferFrom(address _from, address _to, uint256 _tokenId) external /*payable*/;
          \t/// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
          \t///  TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
          \t///  THEY MAY BE PERMANENTLY LOST
          \t/// @dev Throws unless `msg.sender` is the current owner, an authorized
          \t///  operator, or the approved address for this NFT. Throws if `_from` is
          \t///  not the current owner. Throws if `_to` is the zero address. Throws if
          \t///  `_tokenId` is not a valid NFT.
          \t/// @param _from The current owner of the NFT
          \t/// @param _to The new owner
          \t/// @param _tokenId The NFT to transfer
          \tfunction transferFrom(address _from, address _to, uint256 _tokenId) external /*payable*/;
          \t/// @notice Change or reaffirm the approved address for an NFT
          \t/// @dev The zero address indicates there is no approved address.
          \t///  Throws unless `msg.sender` is the current NFT owner, or an authorized
          \t///  operator of the current owner.
          \t/// @param _approved The new approved NFT controller
          \t/// @param _tokenId The NFT to approve
          \tfunction approve(address _approved, uint256 _tokenId) external /*payable*/;
          \t/// @notice Enable or disable approval for a third party ("operator") to manage
          \t///  all of `msg.sender`'s assets
          \t/// @dev Emits the ApprovalForAll event. The contract MUST allow
          \t///  multiple operators per owner.
          \t/// @param _operator Address to add to the set of authorized operators
          \t/// @param _approved True if the operator is approved, false to revoke approval
          \tfunction setApprovalForAll(address _operator, bool _approved) external;
          \t/// @notice Get the approved address for a single NFT
          \t/// @dev Throws if `_tokenId` is not a valid NFT.
          \t/// @param _tokenId The NFT to find the approved address for
          \t/// @return The approved address for this NFT, or the zero address if there is none
          \tfunction getApproved(uint256 _tokenId) external view returns (address);
          \t/// @notice Query if an address is an authorized operator for another address
          \t/// @param _owner The address that owns the NFTs
          \t/// @param _operator The address that acts on behalf of the owner
          \t/// @return True if `_operator` is an approved operator for `_owner`, false otherwise
          \tfunction isApprovedForAll(address _owner, address _operator) external view returns (bool);
          }
          /// @dev Note: the ERC-165 identifier for this interface is 0x150b7a02.
          interface ERC721TokenReceiver {
          \t/// @notice Handle the receipt of an NFT
          \t/// @dev The ERC721 smart contract calls this function on the recipient
          \t///  after a `transfer`. This function MAY throw to revert and reject the
          \t///  transfer. Return of other than the magic value MUST result in the
          \t///  transaction being reverted.
          \t///  Note: the contract address is always the message sender.
          \t/// @param _operator The address which called `safeTransferFrom` function
          \t/// @param _from The address which previously owned the token
          \t/// @param _tokenId The NFT identifier which is being transferred
          \t/// @param _data Additional data with no specified format
          \t/// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
          \t///  unless throwing
          \tfunction onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) external returns(bytes4);
          }
          /**
           * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
           *
           * @notice See https://eips.ethereum.org/EIPS/eip-721
           *
           * @dev The ERC-165 identifier for this interface is 0x5b5e139f.
           *
           * @author William Entriken, Dieter Shirley, Jacob Evans, Nastassia Sachs
           */
          interface ERC721Metadata is ERC721 {
          \t/// @notice A descriptive name for a collection of NFTs in this contract
          \tfunction name() external view returns (string memory _name);
          \t/// @notice An abbreviated name for NFTs in this contract
          \tfunction symbol() external view returns (string memory _symbol);
          \t/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
          \t/// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
          \t///  3986. The URI may point to a JSON file that conforms to the "ERC721
          \t///  Metadata JSON Schema".
          \tfunction tokenURI(uint256 _tokenId) external view returns (string memory);
          }
          /**
           * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
           *
           * @notice See https://eips.ethereum.org/EIPS/eip-721
           *
           * @dev The ERC-165 identifier for this interface is 0x780e9d63.
           *
           * @author William Entriken, Dieter Shirley, Jacob Evans, Nastassia Sachs
           */
          interface ERC721Enumerable is ERC721 {
          \t/// @notice Count NFTs tracked by this contract
          \t/// @return A count of valid NFTs tracked by this contract, where each one of
          \t///  them has an assigned and queryable owner not equal to the zero address
          \tfunction totalSupply() external view returns (uint256);
          \t/// @notice Enumerate valid NFTs
          \t/// @dev Throws if `_index` >= `totalSupply()`.
          \t/// @param _index A counter less than `totalSupply()`
          \t/// @return The token identifier for the `_index`th NFT,
          \t///  (sort order not specified)
          \tfunction tokenByIndex(uint256 _index) external view returns (uint256);
          \t/// @notice Enumerate NFTs assigned to an owner
          \t/// @dev Throws if `_index` >= `balanceOf(_owner)` or if
          \t///  `_owner` is the zero address, representing invalid NFTs.
          \t/// @param _owner An address where we are interested in NFTs owned by them
          \t/// @param _index A counter less than `balanceOf(_owner)`
          \t/// @return The token identifier for the `_index`th NFT assigned to `_owner`,
          \t///   (sort order not specified)
          \tfunction tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.7;
          /**
           * @title Alethea Mintable ERC721
           *
           * @notice Defines mint capabilities for Alethea ERC721 tokens.
           *      This interface should be treated as a definition of what mintable means for ERC721
           */
          interface MintableERC721 {
          \t/**
          \t * @notice Checks if specified token exists
          \t *
          \t * @dev Returns whether the specified token ID has an ownership
          \t *      information associated with it
          \t *
          \t * @param _tokenId ID of the token to query existence for
          \t * @return whether the token exists (true - exists, false - doesn't exist)
          \t */
          \tfunction exists(uint256 _tokenId) external view returns(bool);
          \t/**
          \t * @dev Creates new token with token ID specified
          \t *      and assigns an ownership `_to` for this token
          \t *
          \t * @dev Unsafe: doesn't execute `onERC721Received` on the receiver.
          \t *      Prefer the use of `saveMint` instead of `mint`.
          \t *
          \t * @dev Should have a restricted access handled by the implementation
          \t *
          \t * @param _to an address to mint token to
          \t * @param _tokenId ID of the token to mint
          \t */
          \tfunction mint(address _to, uint256 _tokenId) external;
          \t/**
          \t * @dev Creates new tokens starting with token ID specified
          \t *      and assigns an ownership `_to` for these tokens
          \t *
          \t * @dev Token IDs to be minted: [_tokenId, _tokenId + n)
          \t *
          \t * @dev n must be greater or equal 2: `n > 1`
          \t *
          \t * @dev Unsafe: doesn't execute `onERC721Received` on the receiver.
          \t *      Prefer the use of `saveMintBatch` instead of `mintBatch`.
          \t *
          \t * @dev Should have a restricted access handled by the implementation
          \t *
          \t * @param _to an address to mint tokens to
          \t * @param _tokenId ID of the first token to mint
          \t * @param n how many tokens to mint, sequentially increasing the _tokenId
          \t */
          \tfunction mintBatch(address _to, uint256 _tokenId, uint256 n) external;
          \t/**
          \t * @dev Creates new token with token ID specified
          \t *      and assigns an ownership `_to` for this token
          \t *
          \t * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls
          \t *      `onERC721Received` on `_to` and throws if the return value is not
          \t *      `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
          \t *
          \t * @dev Should have a restricted access handled by the implementation
          \t *
          \t * @param _to an address to mint token to
          \t * @param _tokenId ID of the token to mint
          \t */
          \tfunction safeMint(address _to, uint256 _tokenId) external;
          \t/**
          \t * @dev Creates new token with token ID specified
          \t *      and assigns an ownership `_to` for this token
          \t *
          \t * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls
          \t *      `onERC721Received` on `_to` and throws if the return value is not
          \t *      `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
          \t *
          \t * @dev Should have a restricted access handled by the implementation
          \t *
          \t * @param _to an address to mint token to
          \t * @param _tokenId ID of the token to mint
          \t * @param _data additional data with no specified format, sent in call to `_to`
          \t */
          \tfunction safeMint(address _to, uint256 _tokenId, bytes memory _data) external;
          \t/**
          \t * @dev Creates new tokens starting with token ID specified
          \t *      and assigns an ownership `_to` for these tokens
          \t *
          \t * @dev Token IDs to be minted: [_tokenId, _tokenId + n)
          \t *
          \t * @dev n must be greater or equal 2: `n > 1`
          \t *
          \t * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls
          \t *      `onERC721Received` on `_to` and throws if the return value is not
          \t *      `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
          \t *
          \t * @dev Should have a restricted access handled by the implementation
          \t *
          \t * @param _to an address to mint token to
          \t * @param _tokenId ID of the token to mint
          \t * @param n how many tokens to mint, sequentially increasing the _tokenId
          \t */
          \tfunction safeMintBatch(address _to, uint256 _tokenId, uint256 n) external;
          \t/**
          \t * @dev Creates new tokens starting with token ID specified
          \t *      and assigns an ownership `_to` for these tokens
          \t *
          \t * @dev Token IDs to be minted: [_tokenId, _tokenId + n)
          \t *
          \t * @dev n must be greater or equal 2: `n > 1`
          \t *
          \t * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls
          \t *      `onERC721Received` on `_to` and throws if the return value is not
          \t *      `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
          \t *
          \t * @dev Should have a restricted access handled by the implementation
          \t *
          \t * @param _to an address to mint token to
          \t * @param _tokenId ID of the token to mint
          \t * @param n how many tokens to mint, sequentially increasing the _tokenId
          \t * @param _data additional data with no specified format, sent in call to `_to`
          \t */
          \tfunction safeMintBatch(address _to, uint256 _tokenId, uint256 n, bytes memory _data) external;
          }
          /**
           * @title Alethea Burnable ERC721
           *
           * @notice Defines burn capabilities for Alethea ERC721 tokens.
           *      This interface should be treated as a definition of what burnable means for ERC721
           */
          interface BurnableERC721 {
          \t/**
          \t * @notice Destroys the token with token ID specified
          \t *
          \t * @dev Should be accessible publicly by token owners.
          \t *      May have a restricted access handled by the implementation
          \t *
          \t * @param _tokenId ID of the token to burn
          \t */
          \tfunction burn(uint256 _tokenId) external;
          }
          /**
           * @title With Base URI
           *
           * @notice A marker interface for the contracts having the baseURI() function
           *      or public string variable named baseURI
           *      NFT implementations like TinyERC721, or ShortERC721 are example of such smart contracts
           */
          interface WithBaseURI {
          \t/**
          \t * @dev Usually used in NFT implementations to construct ERC721Metadata.tokenURI as
          \t *      `base URI + token ID` if token URI is not set (not present in `_tokenURIs` mapping)
          \t *
          \t * @dev For example, if base URI is https://api.com/token/, then token #1
          \t *      will have an URI https://api.com/token/1
          \t */
          \tfunction baseURI() external view returns(string memory);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.7;
          /**
           * @title Address Utils
           *
           * @dev Utility library of inline functions on addresses
           *
           * @dev Copy of the Zeppelin's library:
           *      https://github.com/gnosis/openzeppelin-solidity/blob/master/contracts/AddressUtils.sol
           */
          library AddressUtils {
          \t/**
          \t * @notice Checks if the target address is a contract
          \t *
          \t * @dev It is unsafe to assume that an address for which this function returns
          \t *      false is an externally-owned account (EOA) and not a contract.
          \t *
          \t * @dev Among others, `isContract` will return false for the following
          \t *      types of addresses:
          \t *        - an externally-owned account
          \t *        - a contract in construction
          \t *        - an address where a contract will be created
          \t *        - an address where a contract lived, but was destroyed
          \t *
          \t * @param addr address to check
          \t * @return whether the target address is a contract
          \t */
          \tfunction isContract(address addr) internal view returns (bool) {
          \t\t// a variable to load `extcodesize` to
          \t\tuint256 size = 0;
          \t\t// XXX Currently there is no better way to check if there is a contract in an address
          \t\t// than to check the size of the code at that address.
          \t\t// See https://ethereum.stackexchange.com/a/14016/36603 for more details about how this works.
          \t\t// TODO: Check this again before the Serenity release, because all addresses will be contracts.
          \t\t// solium-disable-next-line security/no-inline-assembly
          \t\tassembly {
          \t\t\t// retrieve the size of the code at address `addr`
          \t\t\tsize := extcodesize(addr)
          \t\t}
          \t\t// positive size indicates a smart contract address
          \t\treturn size > 0;
          \t}
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.7;
          /**
           * @title Array Utils
           *
           * @notice Solidity doesn't always work with arrays in an optimal way.
           *      This library collects functions helping to optimize gas usage
           *      when working with arrays in Solidity.
           *
           * @dev One of the most important use cases for arrays is "tight" arrays -
           *      arrays which store values significantly less than 256-bits numbers
           */
          library ArrayUtils {
          \t/**
          \t * @dev Pushes `n` 32-bits values sequentially into storage allocated array `data`
          \t *      starting from the 32-bits value `v0`
          \t *
          \t * @dev Optimizations comparing to non-assembly implementation:
          \t *      - reads+writes to array size slot only once (instead of `n` times)
          \t *      - reads from the array data slots only once (instead of `7n/8` times)
          \t *      - writes into array data slots `n/8` times (instead of `n` times)
          \t *
          \t * @dev Maximum gas saving estimate: ~3n sstore, or 15,000 * n
          \t *
          \t * @param data storage array pointer to an array of 32-bits elements
          \t * @param v0 first number to push into the array
          \t * @param n number of values to push, pushes [v0, ..., v0 + n - 1]
          \t */
          \tfunction push32(uint32[] storage data, uint32 v0, uint32 n) internal {
          \t\t// we're going to write 32-bits values into 256-bits storage slots of the array
          \t\t// each 256-slot can store up to 8 32-bits sub-blocks, it can also be partially empty
          \t\tassembly {
          \t\t\t// for dynamic arrays their slot (array.slot) contains the array length
          \t\t\t// array data is stored separately in consequent storage slots starting
          \t\t\t// from the slot with the address keccak256(array.slot)
          \t\t\t// read the array length into `len` and increase it by `n`
          \t\t\tlet len := sload(data.slot)
          \t\t\tsstore(data.slot, add(len, n))
          \t\t\t// find where to write elements and store this location into `loc`
          \t\t\t// load array storage slot number into memory onto position 0,
          \t\t\t// calculate the keccak256 of the slot number (first 32 bytes at position 0)
          \t\t\t// - this will point to the beginning of the array,
          \t\t\t// so we add array length divided by 8 to point to the last array slot
          \t\t\tmstore(0, data.slot)
          \t\t\tlet loc := add(keccak256(0, 32), div(len, 8))
          \t\t\t// if we start writing data into already partially occupied slot (`len % 8 != 0`)
          \t\t\t// we need to modify the contents of that slot: read it and rewrite it
          \t\t\tlet offset := mod(len, 8)
          \t\t\tif not(iszero(offset)) {
          \t\t\t\t// how many 32-bits sub-blocks left in the slot
          \t\t\t\tlet left := sub(8, offset)
          \t\t\t\t// update the `left` value not to exceed `n`
          \t\t\t\tif gt(left, n) { left := n }
          \t\t\t\t// load the contents of the first slot (partially occupied)
          \t\t\t\tlet v256 := sload(loc)
          \t\t\t\t// write the slot in 32-bits sub-blocks
          \t\t\t\tfor { let j := 0 } lt(j, left) { j := add(j, 1) } {
          \t\t\t\t\t// write sub-block `j` at offset: `(j + offset) * 32` bits, length: 32-bits
          \t\t\t\t\t// v256 |= (v0 + j) << (j + offset) * 32
          \t\t\t\t\tv256 := or(v256, shl(mul(add(j, offset), 32), add(v0, j)))
          \t\t\t\t}
          \t\t\t\t// write first slot back, it can be still partially occupied, it can also be full
          \t\t\t\tsstore(loc, v256)
          \t\t\t\t// update `loc`: move to the next slot
          \t\t\t\tloc := add(loc, 1)
          \t\t\t\t// update `v0`: increment by number of values pushed
          \t\t\t\tv0 := add(v0, left)
          \t\t\t\t// update `n`: decrement by number of values pushed
          \t\t\t\tn := sub(n, left)
          \t\t\t}
          \t\t\t// rest of the slots (if any) are empty and will be only written to
          \t\t\t// write the array in 256-bits (8x32) slots
          \t\t\t// `i` iterates [0, n) with the 256-bits step, which is 8 taken `n` is 32-bits long
          \t\t\tfor { let i := 0 } lt(i, n) { i := add(i, 8) } {
          \t\t\t\t// how many 32-bits sub-blocks left in the slot
          \t\t\t\tlet left := 8
          \t\t\t\t// update the `left` value not to exceed `n`
          \t\t\t\tif gt(left, n) { left := n }
          \t\t\t\t// init the 256-bits slot value
          \t\t\t\tlet v256 := 0
          \t\t\t\t// write the slot in 32-bits sub-blocks
          \t\t\t\tfor { let j := 0 } lt(j, left) { j := add(j, 1) } {
          \t\t\t\t\t// write sub-block `j` at offset: `j * 32` bits, length: 32-bits
          \t\t\t\t\t// v256 |= (v0 + i + j) << j * 32
          \t\t\t\t\tv256 := or(v256, shl(mul(j, 32), add(v0, add(i, j))))
          \t\t\t\t}
          \t\t\t\t// write slot `i / 8`
          \t\t\t\tsstore(add(loc, div(i, 8)), v256)
          \t\t\t}
          \t\t}
          \t}
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.7;
          /**
           * @title String Utils Library
           *
           * @dev Library for working with strings, primarily converting
           *      between strings and integer types
           */
          library StringUtils {
          \t/**
          \t * @dev Converts a string to unsigned integer using the specified `base`
          \t * @dev Throws on invalid input
          \t *      (wrong characters for a given `base`)
          \t * @dev Throws if given `base` is not supported
          \t * @param a string to convert
          \t * @param base number base, one of 2, 8, 10, 16
          \t * @return i a number representing given string
          \t */
          \tfunction atoi(string memory a, uint8 base) internal pure returns (uint256 i) {
          \t\t// check if the base is valid
          \t\trequire(base == 2 || base == 8 || base == 10 || base == 16);
          \t\t// convert string into bytes for convenient iteration
          \t\tbytes memory buf = bytes(a);
          \t\t// iterate over the string (bytes buffer)
          \t\tfor(uint256 p = 0; p < buf.length; p++) {
          \t\t\t// extract the digit
          \t\t\tuint8 digit = uint8(buf[p]) - 0x30;
          \t\t\t// if digit is greater then 10 - mind the gap
          \t\t\t// see `itoa` function for more details
          \t\t\tif(digit > 10) {
          \t\t\t\t// remove the gap
          \t\t\t\tdigit -= 7;
          \t\t\t}
          \t\t\t// check if digit meets the base
          \t\t\trequire(digit < base);
          \t\t\t// move to the next digit slot
          \t\t\ti *= base;
          \t\t\t// add digit to the result
          \t\t\ti += digit;
          \t\t}
          \t\t// return the result
          \t\treturn i;
          \t}
          \t/**
          \t * @dev Converts a integer to a string using the specified `base`
          \t * @dev Throws if given `base` is not supported
          \t * @param i integer to convert
          \t * @param base number base, one of 2, 8, 10, 16
          \t * @return a a string representing given integer
          \t */
          \tfunction itoa(uint256 i, uint8 base) internal pure returns (string memory a) {
          \t\t// check if the base is valid
          \t\trequire(base == 2 || base == 8 || base == 10 || base == 16);
          \t\t// for zero input the result is "0" string for any base
          \t\tif(i == 0) {
          \t\t\treturn "0";
          \t\t}
          \t\t// bytes buffer to put ASCII characters into
          \t\tbytes memory buf = new bytes(256);
          \t\t// position within a buffer to be used in cycle
          \t\tuint256 p = 0;
          \t\t// extract digits one by one in a cycle
          \t\twhile(i > 0) {
          \t\t\t// extract current digit
          \t\t\tuint8 digit = uint8(i % base);
          \t\t\t// convert it to an ASCII code
          \t\t\t// 0x20 is " "
          \t\t\t// 0x30-0x39 is "0"-"9"
          \t\t\t// 0x41-0x5A is "A"-"Z"
          \t\t\t// 0x61-0x7A is "a"-"z" ("A"-"Z" XOR " ")
          \t\t\tuint8 ascii = digit + 0x30;
          \t\t\t// if digit is greater then 10,
          \t\t\t// fix the 0x3A-0x40 gap of punctuation marks
          \t\t\t// (7 characters in ASCII table)
          \t\t\tif(digit >= 10) {
          \t\t\t\t// jump through the gap
          \t\t\t\tascii += 7;
          \t\t\t}
          \t\t\t// write character into the buffer
          \t\t\tbuf[p++] = bytes1(ascii);
          \t\t\t// move to the next digit
          \t\t\ti /= base;
          \t\t}
          \t\t// `p` contains real length of the buffer now,
          \t\t// allocate the resulting buffer of that size
          \t\tbytes memory result = new bytes(p);
          \t\t// copy the buffer in the reversed order
          \t\tfor(p = 0; p < result.length; p++) {
          \t\t\t// copy from the beginning of the original buffer
          \t\t\t// to the end of resulting smaller buffer
          \t\t\tresult[result.length - p - 1] = buf[p];
          \t\t}
          \t\t// construct string and return
          \t\treturn string(result);
          \t}
          \t/**
          \t * @dev Concatenates two strings `s1` and `s2`, for example, if
          \t *      `s1` == `foo` and `s2` == `bar`, the result `s` == `foobar`
          \t * @param s1 first string
          \t * @param s2 second string
          \t * @return s concatenation result s1 + s2
          \t */
          \tfunction concat(string memory s1, string memory s2) internal pure returns (string memory s) {
          \t\t// an old way of string concatenation (Solidity 0.4) is commented out
          /*
          \t\t// convert s1 into buffer 1
          \t\tbytes memory buf1 = bytes(s1);
          \t\t// convert s2 into buffer 2
          \t\tbytes memory buf2 = bytes(s2);
          \t\t// create a buffer for concatenation result
          \t\tbytes memory buf = new bytes(buf1.length + buf2.length);
          \t\t// copy buffer 1 into buffer
          \t\tfor(uint256 i = 0; i < buf1.length; i++) {
          \t\t\tbuf[i] = buf1[i];
          \t\t}
          \t\t// copy buffer 2 into buffer
          \t\tfor(uint256 j = buf1.length; j < buf2.length; j++) {
          \t\t\tbuf[j] = buf2[j - buf1.length];
          \t\t}
          \t\t// construct string and return
          \t\treturn string(buf);
          */
          \t\t// simply use built in function
          \t\treturn string(abi.encodePacked(s1, s2));
          \t}
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.7;
          /**
           * @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.
           *
           * @dev Copy of the Zeppelin's library:
           *      https://github.com/OpenZeppelin/openzeppelin-contracts/blob/b0cf6fbb7a70f31527f36579ad644e1cf12fdf4e/contracts/utils/cryptography/ECDSA.sol
           */
          library ECDSA {
          \t/**
          \t * @dev Returns the address that signed a hashed message (`hash`) with
          \t * `signature`. This address can then be used for verification purposes.
          \t *
          \t * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
          \t * this function rejects them by requiring the `s` value to be in the lower
          \t * half order, and the `v` value to be either 27 or 28.
          \t *
          \t * IMPORTANT: `hash` _must_ be the result of a hash operation for the
          \t * verification to be secure: it is possible to craft signatures that
          \t * recover to arbitrary addresses for non-hashed data. A safe way to ensure
          \t * this is by receiving a hash of the original message (which may otherwise
          \t * be too long), and then calling {toEthSignedMessageHash} on it.
          \t *
          \t * Documentation for signature generation:
          \t * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
          \t * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
          \t */
          \tfunction recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
          \t\t// Divide the signature in r, s and v variables
          \t\tbytes32 r;
          \t\tbytes32 s;
          \t\tuint8 v;
          \t\t// Check the signature length
          \t\t// - case 65: r,s,v signature (standard)
          \t\t// - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
          \t\tif (signature.length == 65) {
          \t\t\t// ecrecover takes the signature parameters, and the only way to get them
          \t\t\t// currently is to use assembly.
          \t\t\tassembly {
          \t\t\t\tr := mload(add(signature, 0x20))
          \t\t\t\ts := mload(add(signature, 0x40))
          \t\t\t\tv := byte(0, mload(add(signature, 0x60)))
          \t\t\t}
          \t\t}
          \t\telse if (signature.length == 64) {
          \t\t\t// ecrecover takes the signature parameters, and the only way to get them
          \t\t\t// currently is to use assembly.
          \t\t\tassembly {
          \t\t\t\tlet vs := mload(add(signature, 0x40))
          \t\t\t\tr := mload(add(signature, 0x20))
          \t\t\t\ts := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
          \t\t\t\tv := add(shr(255, vs), 27)
          \t\t\t}
          \t\t}
          \t\telse {
          \t\t\trevert("invalid signature length");
          \t\t}
          \t\treturn recover(hash, v, r, s);
          \t}
          \t/**
          \t * @dev Overload of {ECDSA-recover} that receives the `v`,
          \t * `r` and `s` signature fields separately.
          \t */
          \tfunction recover(
          \t\tbytes32 hash,
          \t\tuint8 v,
          \t\tbytes32 r,
          \t\tbytes32 s
          \t) internal pure returns (address) {
          \t\t// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
          \t\t// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
          \t\t// the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
          \t\t// signatures from current libraries generate a unique signature with an s-value in the lower half order.
          \t\t//
          \t\t// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
          \t\t// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
          \t\t// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
          \t\t// these malleable signatures as well.
          \t\trequire(
          \t\t\tuint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
          \t\t\t"invalid signature 's' value"
          \t\t);
          \t\trequire(v == 27 || v == 28, "invalid signature 'v' value");
          \t\t// If the signature is valid (and not malleable), return the signer address
          \t\taddress signer = ecrecover(hash, v, r, s);
          \t\trequire(signer != address(0), "invalid signature");
          \t\treturn signer;
          \t}
          \t/**
          \t * @dev Returns an Ethereum Signed Message, created from a `hash`. This
          \t * produces hash corresponding to the one signed with the
          \t * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
          \t * JSON-RPC method as part of EIP-191.
          \t *
          \t * See {recover}.
          \t */
          \tfunction toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
          \t\t// 32 is the length in bytes of hash,
          \t\t// enforced by the type signature above
          \t\treturn keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
          32", hash));
          \t}
          \t/**
          \t * @dev Returns an Ethereum Signed Typed Data, created from a
          \t * `domainSeparator` and a `structHash`. This produces hash corresponding
          \t * to the one signed with the
          \t * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
          \t * JSON-RPC method as part of EIP-712.
          \t *
          \t * See {recover}.
          \t */
          \tfunction toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
          \t\treturn keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
          \t}
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.7;
          /**
           * @title Access Control List
           *
           * @notice Access control smart contract provides an API to check
           *      if specific operation is permitted globally and/or
           *      if particular user has a permission to execute it.
           *
           * @notice It deals with two main entities: features and roles.
           *
           * @notice Features are designed to be used to enable/disable specific
           *      functions (public functions) of the smart contract for everyone.
           * @notice User roles are designed to restrict access to specific
           *      functions (restricted functions) of the smart contract to some users.
           *
           * @notice Terms "role", "permissions" and "set of permissions" have equal meaning
           *      in the documentation text and may be used interchangeably.
           * @notice Terms "permission", "single permission" implies only one permission bit set.
           *
           * @notice Access manager is a special role which allows to grant/revoke other roles.
           *      Access managers can only grant/revoke permissions which they have themselves.
           *      As an example, access manager with no other roles set can only grant/revoke its own
           *      access manager permission and nothing else.
           *
           * @notice Access manager permission should be treated carefully, as a super admin permission:
           *      Access manager with even no other permission can interfere with another account by
           *      granting own access manager permission to it and effectively creating more powerful
           *      permission set than its own.
           *
           * @dev Both current and OpenZeppelin AccessControl implementations feature a similar API
           *      to check/know "who is allowed to do this thing".
           * @dev Zeppelin implementation is more flexible:
           *      - it allows setting unlimited number of roles, while current is limited to 256 different roles
           *      - it allows setting an admin for each role, while current allows having only one global admin
           * @dev Current implementation is more lightweight:
           *      - it uses only 1 bit per role, while Zeppelin uses 256 bits
           *      - it allows setting up to 256 roles at once, in a single transaction, while Zeppelin allows
           *        setting only one role in a single transaction
           *
           * @dev This smart contract is designed to be inherited by other
           *      smart contracts which require access control management capabilities.
           *
           * @dev Access manager permission has a bit 255 set.
           *      This bit must not be used by inheriting contracts for any other permissions/features.
           */
          contract AccessControl {
          \t/**
          \t * @notice Access manager is responsible for assigning the roles to users,
          \t *      enabling/disabling global features of the smart contract
          \t * @notice Access manager can add, remove and update user roles,
          \t *      remove and update global features
          \t *
          \t * @dev Role ROLE_ACCESS_MANAGER allows modifying user roles and global features
          \t * @dev Role ROLE_ACCESS_MANAGER has single bit at position 255 enabled
          \t */
          \tuint256 public constant ROLE_ACCESS_MANAGER = 0x8000000000000000000000000000000000000000000000000000000000000000;
          \t/**
          \t * @dev Bitmask representing all the possible permissions (super admin role)
          \t * @dev Has all the bits are enabled (2^256 - 1 value)
          \t */
          \tuint256 private constant FULL_PRIVILEGES_MASK = type(uint256).max; // before 0.8.0: uint256(-1) overflows to 0xFFFF...
          \t/**
          \t * @notice Privileged addresses with defined roles/permissions
          \t * @notice In the context of ERC20/ERC721 tokens these can be permissions to
          \t *      allow minting or burning tokens, transferring on behalf and so on
          \t *
          \t * @dev Maps user address to the permissions bitmask (role), where each bit
          \t *      represents a permission
          \t * @dev Bitmask 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
          \t *      represents all possible permissions
          \t * @dev 'This' address mapping represents global features of the smart contract
          \t */
          \tmapping(address => uint256) public userRoles;
          \t/**
          \t * @dev Fired in updateRole() and updateFeatures()
          \t *
          \t * @param _by operator which called the function
          \t * @param _to address which was granted/revoked permissions
          \t * @param _requested permissions requested
          \t * @param _actual permissions effectively set
          \t */
          \tevent RoleUpdated(address indexed _by, address indexed _to, uint256 _requested, uint256 _actual);
          \t/**
          \t * @notice Creates an access control instance,
          \t *      setting contract creator to have full privileges
          \t */
          \tconstructor() {
          \t\t// contract creator has full privileges
          \t\tuserRoles[msg.sender] = FULL_PRIVILEGES_MASK;
          \t}
          \t/**
          \t * @notice Retrieves globally set of features enabled
          \t *
          \t * @dev Effectively reads userRoles role for the contract itself
          \t *
          \t * @return 256-bit bitmask of the features enabled
          \t */
          \tfunction features() public view returns(uint256) {
          \t\t// features are stored in 'this' address  mapping of `userRoles` structure
          \t\treturn userRoles[address(this)];
          \t}
          \t/**
          \t * @notice Updates set of the globally enabled features (`features`),
          \t *      taking into account sender's permissions
          \t *
          \t * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
          \t * @dev Function is left for backward compatibility with older versions
          \t *
          \t * @param _mask bitmask representing a set of features to enable/disable
          \t */
          \tfunction updateFeatures(uint256 _mask) public {
          \t\t// delegate call to `updateRole`
          \t\tupdateRole(address(this), _mask);
          \t}
          \t/**
          \t * @notice Updates set of permissions (role) for a given user,
          \t *      taking into account sender's permissions.
          \t *
          \t * @dev Setting role to zero is equivalent to removing an all permissions
          \t * @dev Setting role to `FULL_PRIVILEGES_MASK` is equivalent to
          \t *      copying senders' permissions (role) to the user
          \t * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
          \t *
          \t * @param operator address of a user to alter permissions for or zero
          \t *      to alter global features of the smart contract
          \t * @param role bitmask representing a set of permissions to
          \t *      enable/disable for a user specified
          \t */
          \tfunction updateRole(address operator, uint256 role) public {
          \t\t// caller must have a permission to update user roles
          \t\trequire(isSenderInRole(ROLE_ACCESS_MANAGER), "access denied");
          \t\t// evaluate the role and reassign it
          \t\tuserRoles[operator] = evaluateBy(msg.sender, userRoles[operator], role);
          \t\t// fire an event
          \t\temit RoleUpdated(msg.sender, operator, role, userRoles[operator]);
          \t}
          \t/**
          \t * @notice Determines the permission bitmask an operator can set on the
          \t *      target permission set
          \t * @notice Used to calculate the permission bitmask to be set when requested
          \t *     in `updateRole` and `updateFeatures` functions
          \t *
          \t * @dev Calculated based on:
          \t *      1) operator's own permission set read from userRoles[operator]
          \t *      2) target permission set - what is already set on the target
          \t *      3) desired permission set - what do we want set target to
          \t *
          \t * @dev Corner cases:
          \t *      1) Operator is super admin and its permission set is `FULL_PRIVILEGES_MASK`:
          \t *        `desired` bitset is returned regardless of the `target` permission set value
          \t *        (what operator sets is what they get)
          \t *      2) Operator with no permissions (zero bitset):
          \t *        `target` bitset is returned regardless of the `desired` value
          \t *        (operator has no authority and cannot modify anything)
          \t *
          \t * @dev Example:
          \t *      Consider an operator with the permissions bitmask     00001111
          \t *      is about to modify the target permission set          01010101
          \t *      Operator wants to set that permission set to          00110011
          \t *      Based on their role, an operator has the permissions
          \t *      to update only lowest 4 bits on the target, meaning that
          \t *      high 4 bits of the target set in this example is left
          \t *      unchanged and low 4 bits get changed as desired:      01010011
          \t *
          \t * @param operator address of the contract operator which is about to set the permissions
          \t * @param target input set of permissions to operator is going to modify
          \t * @param desired desired set of permissions operator would like to set
          \t * @return resulting set of permissions given operator will set
          \t */
          \tfunction evaluateBy(address operator, uint256 target, uint256 desired) public view returns(uint256) {
          \t\t// read operator's permissions
          \t\tuint256 p = userRoles[operator];
          \t\t// taking into account operator's permissions,
          \t\t// 1) enable the permissions desired on the `target`
          \t\ttarget |= p & desired;
          \t\t// 2) disable the permissions desired on the `target`
          \t\ttarget &= FULL_PRIVILEGES_MASK ^ (p & (FULL_PRIVILEGES_MASK ^ desired));
          \t\t// return calculated result
          \t\treturn target;
          \t}
          \t/**
          \t * @notice Checks if requested set of features is enabled globally on the contract
          \t *
          \t * @param required set of features to check against
          \t * @return true if all the features requested are enabled, false otherwise
          \t */
          \tfunction isFeatureEnabled(uint256 required) public view returns(bool) {
          \t\t// delegate call to `__hasRole`, passing `features` property
          \t\treturn __hasRole(features(), required);
          \t}
          \t/**
          \t * @notice Checks if transaction sender `msg.sender` has all the permissions required
          \t *
          \t * @param required set of permissions (role) to check against
          \t * @return true if all the permissions requested are enabled, false otherwise
          \t */
          \tfunction isSenderInRole(uint256 required) public view returns(bool) {
          \t\t// delegate call to `isOperatorInRole`, passing transaction sender
          \t\treturn isOperatorInRole(msg.sender, required);
          \t}
          \t/**
          \t * @notice Checks if operator has all the permissions (role) required
          \t *
          \t * @param operator address of the user to check role for
          \t * @param required set of permissions (role) to check
          \t * @return true if all the permissions requested are enabled, false otherwise
          \t */
          \tfunction isOperatorInRole(address operator, uint256 required) public view returns(bool) {
          \t\t// delegate call to `__hasRole`, passing operator's permissions (role)
          \t\treturn __hasRole(userRoles[operator], required);
          \t}
          \t/**
          \t * @dev Checks if role `actual` contains all the permissions required `required`
          \t *
          \t * @param actual existent role
          \t * @param required required role
          \t * @return true if actual has required role (all permissions), false otherwise
          \t */
          \tfunction __hasRole(uint256 actual, uint256 required) internal pure returns(bool) {
          \t\t// check the bitmask for the role required and return the result
          \t\treturn actual & required == required;
          \t}
          }
          

          File 3 of 5: OpenSeaFactoryImpl
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.7;
          import "../interfaces/ERC165Spec.sol";
          import "../interfaces/ERC721Spec.sol";
          import "../interfaces/AletheaERC721Spec.sol";
          import "../lib/StringUtils.sol";
          import "../utils/AccessControl.sol";
          /**
           * @title OpenSea ERC721 Factory interface
           *
           * @notice In order to mint items only when they're purchased, OpenSea provides a Factory interface
           *      that is used to define how the items will be minted.
           *      See https://docs.opensea.io/docs/2-custom-item-sale-contract
           *
           * @notice This is a generic factory contract that can be used to mint tokens. The configuration
           *      for minting is specified by an _optionId, which can be used to delineate various
           *      ways of minting.
           *
           * @dev Copy of the OpenSea interface:
           *      https://github.com/ProjectOpenSea/opensea-creatures/blob/master/contracts/IFactoryERC721.sol
           */
          interface OpenSeaFactoryERC721 is ERC165 {
          \t/**
          \t * @dev Returns the name of this factory.
          \t */
          \tfunction name() external view returns (string memory);
          \t/**
          \t * @dev Returns the symbol for this factory.
          \t */
          \tfunction symbol() external view returns (string memory);
          \t/**
          \t * @dev Number of options the factory supports.
          \t */
          \tfunction numOptions() external view returns (uint256);
          \t/**
          \t * @dev Returns whether the option ID can be minted. Can return false if the developer wishes to
          \t *      restrict a total supply per option ID (or overall).
          \t */
          \tfunction canMint(uint256 _optionId) external view returns (bool);
          \t/**
          \t * @dev Returns a URL specifying some metadata about the option. This metadata can be of the
          \t *      same structure as the ERC721 metadata.
          \t */
          \tfunction tokenURI(uint256 _optionId) external view returns (string memory);
          \t/**
          \t * @dev Indicates that this is a factory contract. Ideally would use EIP 165 supportsInterface()
          \t */
          \tfunction supportsFactoryInterface() external view returns (bool);
          \t/**
          \t * @dev Mints asset(s) in accordance to a specific address with a particular "option". This should be
          \t *      callable only by the contract owner or the owner's Wyvern Proxy (later universal login will solve this).
          \t *      Options should also be delineated 0 - (numOptions() - 1) for convenient indexing.
          \t * @param _optionId the option id
          \t * @param _toAddress address of the future owner of the asset(s)
          \t */
          \tfunction mint(uint256 _optionId, address _toAddress) external;
          }
          /**
           * @dev An OpenSea delegate proxy interface which we use in ProxyRegistry.
           *      We use it to give compiler a hint that ProxyRegistry.proxies() needs to be
           *      converted to the address type explicitly
           */
          interface OwnableDelegateProxy {}
          /**
           * @dev OpenSea Proxy Registry determines which address (wrapped as OwnableDelegateProxy)
           *      is allowed to mint an option at any given time
           * @dev OpenSea takes care to set it properly when an option is being bought
           */
          interface ProxyRegistry {
          \t/**
          \t * @dev Maps owner address => eligible option minter address wrapped as OwnableDelegateProxy
          \t */
          \tfunction proxies(address) external view returns(OwnableDelegateProxy);
          }
          /**
           * @title OpenSea ERC721 Factory implementation
           *
           * @notice OpenSea Factory interface implementation, NFT minter contract,
           *      powers the OpenSea sale of the 9,900 personalities of the 10k sale campaign
           *
           * @dev Links to PersonalityPodERC721 smart contract on deployment, allows OpenSea to mint
           *      PersonalityPodERC721 from 101 to 10,000 (both bounds inclusive)
           *
           * @dev Each OpenSea Factory option ID is used to signify the minting of one random type of
           *      Personality Pod
           */
          contract OpenSeaFactoryImpl is OpenSeaFactoryERC721, AccessControl {
          \t/**
          \t * @dev OpenSea expects Factory to be "Ownable", that is having an "owner",
          \t *      we introduce a fake "owner" here with no authority
          \t */
          \taddress public owner;
          \t/**
          \t * @dev NFT ERC721 contract address to mint NFTs from and bind to iNFTs created
          \t */
          \taddress public immutable nftContract;
          \t/**
          \t * @dev OpenSea Proxy Registry determines which address is allowed to mint
          \t *      an option at any given time
          \t * @dev OpenSea takes care to set it to the NFT buyer address when they buy an option
          \t */
          \taddress public immutable proxyRegistry;
          \t/**
          \t * @dev Number of options the factory supports,
          \t *      options start from zero and end at `options` (exclusive)
          \t */
          \tuint32 private options;
          \t/**
          \t * @dev Base URI is used to construct option URI as
          \t *      `base URI + option ID`
          \t *
          \t * @dev For example, if base URI is https://api.com/option/, then option #1
          \t *      will have an URI https://api.com/option/1
          \t */
          \tstring public baseURI = "";
          \t/**
          \t * @dev Initialized with the tokenId each optionId should start minting from,
          \t *      incremented each time the option is minted
          \t *
          \t * @dev For each option, [currentTokenId[optionId], tokenIdUpperBound[optionId])
          \t *      is the range of token IDs left to be minted
          \t *
          \t * @dev Maps optionId => next available (current) token ID for an option
          \t */
          \tmapping(uint256 => uint256) public currentTokenId;
          \t/**
          \t * @dev At what tokenId each optionId should end minting at (exclusive)
          \t *
          \t * @dev For each option, [currentTokenId[optionId], tokenIdUpperBound[optionId])
          \t *      is the range of token IDs left to be minted
          \t *
          \t * @dev Maps optionId => final token ID (exclusive) for an option
          \t */
          \tmapping(uint256 => uint256) public tokenIdUpperBound;
          \t/**
          \t * @notice Minter is responsible for creating (minting) iNFTs
          \t *
          \t * @dev Role ROLE_MINTER allows minting iNFTs (calling `mint` function)
          \t */
          \tuint32 public constant ROLE_MINTER = 0x0001_0000;
          \t/**
          \t * @notice URI manager is responsible for managing base URI
          \t *      which is used to construct URIs for each option
          \t *
          \t * @dev Role ROLE_URI_MANAGER allows updating the base URI
          \t *      (executing `setBaseURI` function)
          \t */
          \tuint32 public constant ROLE_URI_MANAGER = 0x0010_0000;
          \t/**
          \t * @notice OpenSea manager is responsible for registering the factory
          \t *      in OpenSea via "Transfer" event mechanism
          \t *
          \t * @dev Role ROLE_OS_MANAGER allows notifying OpenSea about the contract
          \t *      "owner" change via emitting "Transfer" events read by the OpenSea
          \t *      (executing `fireTransferEvents` function)
          \t */
          \tuint32 public constant ROLE_OS_MANAGER = 0x0040_0000;
          \t/**
          \t * @dev Fired in mint() when Alethea NFT is created
          \t *
          \t * @param _by an address which executed the mint function
          \t * @param _optionId OpenSea option ID
          \t * @param _to an address NFT was minted to
          \t */
          \tevent Minted(address indexed _by, uint256 _optionId, address indexed _to);
          \t/**
          \t * @dev Fired in setBaseURI()
          \t *
          \t * @param _by an address which executed update
          \t * @param _oldVal old _baseURI value
          \t * @param _newVal new _baseURI value
          \t */
          \tevent BaseURIUpdated(address indexed _by, string _oldVal, string _newVal);
          \t/**
          \t * @dev An event caught by the OpenSea for automatic factory registration
          \t *      and assigning option "owner" to `to` address defined in the event
          \t * @dev See: OpenSea docs and source code examples,
          \t *      https://docs.opensea.io/docs/2-custom-sale-contract-viewing-your-sale-assets-on-opensea
          \t */
          \tevent Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
          \t/**
          \t * @dev Creates/deploys the factory and binds it to NFT smart contract on construction
          \t *
          \t * @param _nft deployed NFT smart contract address; factory will mint NFTs of that type
          \t * @param _proxyRegistry OpenSea proxy registry address
          \t * @param _rangeBounds token ID ranges foreach option - the initial `currentTokenId` and `tokenIdUpperBound`
          \t */
          \tconstructor(address _nft, address _proxyRegistry, uint32[] memory _rangeBounds) {
          \t\t// verify the inputs are set
          \t\trequire(_nft != address(0), "NFT contract is not set");
          \t\trequire(_proxyRegistry != address(0), "OpenSea proxy registry is not set");
          \t\t// ensure there is at least one option (2 numbers for 1 range)
          \t\trequire(_rangeBounds.length > 1, "invalid number of options");
          \t\t// verify range bound initial element is greater than 100
          \t\trequire(_rangeBounds[0] > 100, "invalid range bound initial element");
          \t\t// verify inputs are valid smart contracts of the expected interfaces
          \t\trequire(ERC165(_nft).supportsInterface(type(ERC721).interfaceId), "unexpected NFT type");
          \t\trequire(ERC165(_nft).supportsInterface(type(ERC721Metadata).interfaceId), "unexpected NFT type");
          \t\trequire(ERC165(_nft).supportsInterface(type(MintableERC721).interfaceId), "unexpected NFT type");
          \t\t// verify that range bounds elements increase (monotonically increasing)
          \t\tfor(uint256 i = 0; i < _rangeBounds.length - 1; i++) {
          \t\t\t// compare current element and next element
          \t\t\trequire(_rangeBounds[i] < _rangeBounds[i + 1], "invalid range bounds");
          \t\t}
          \t\t// assign the NFT address
          \t\tnftContract = _nft;
          \t\t// assign owner
          \t\towner = msg.sender;
          \t\t// assign OpenSea Proxy Registry address
          \t\tproxyRegistry = _proxyRegistry;
          \t\t// number of options is derived from the range bounds array
          \t\toptions = uint32(_rangeBounds.length - 1);
          \t\t// assign the appropriate start and upper bound for each optionId
          \t\tfor(uint256 i = 0; i < _rangeBounds.length - 1; i++) {
          \t\t\tcurrentTokenId[i] = _rangeBounds[i];
          \t\t\ttokenIdUpperBound[i] = _rangeBounds[i + 1];
          \t\t}
          \t\t// fire events for each option
          \t\tfireTransferEvents(address(0), msg.sender);
          \t}
          \t/**
          \t * @dev Restricted access function which updates base URI for optionIds
          \t *
          \t * @param _baseURI new base URI to set
          \t */
          \tfunction setBaseURI(string memory _baseURI) public virtual {
          \t\t// verify the access permission
          \t\trequire(isSenderInRole(ROLE_URI_MANAGER), "access denied");
          \t\t// emit an event first - to log both old and new values
          \t\temit BaseURIUpdated(msg.sender, baseURI, _baseURI);
          \t\t// and update base URI
          \t\tbaseURI = _baseURI;
          \t}
          \t/**
          \t * @inheritdoc ERC165
          \t */
          \tfunction supportsInterface(bytes4 interfaceId) public pure override returns (bool) {
          \t\t// reconstruct from current interface and super interface
          \t\treturn interfaceId == type(OpenSeaFactoryERC721).interfaceId;
          \t}
          \t/**
          \t * @inheritdoc OpenSeaFactoryERC721
          \t */
          \tfunction name() public override view returns (string memory) {
          \t\t// delegate to super implementation
          \t\treturn ERC721Metadata(nftContract).name();
          \t}
          \t/**
          \t * @inheritdoc OpenSeaFactoryERC721
          \t */
          \tfunction symbol() public override view returns (string memory) {
          \t\t// delegate to super implementation
          \t\treturn ERC721Metadata(nftContract).symbol();
          \t}
          \t/**
          \t * @inheritdoc OpenSeaFactoryERC721
          \t */
          \tfunction numOptions() public override view returns (uint256) {
          \t\t// calculate based on 0-indexed options
          \t\treturn options;
          \t}
          \t/**
          \t * @inheritdoc OpenSeaFactoryERC721
          \t */
          \tfunction canMint(uint256 _optionId) public override view returns (bool) {
          \t\t// check valid optionId, bounds
          \t\treturn _optionId < options && currentTokenId[_optionId] < tokenIdUpperBound[_optionId];
          \t}
          \t/**
          \t * @inheritdoc OpenSeaFactoryERC721
          \t */
          \tfunction tokenURI(uint256 _optionId) public override view returns (string memory) {
          \t\t// concatenate base URI + token ID
          \t\treturn StringUtils.concat(baseURI, StringUtils.itoa(_optionId, 10));
          \t}
          \t/**
          \t * @inheritdoc OpenSeaFactoryERC721
          \t */
          \tfunction supportsFactoryInterface() public override pure returns (bool) {
          \t\t// use ERC165 supportsInterface to return `true`
          \t\treturn supportsInterface(type(OpenSeaFactoryERC721).interfaceId);
          \t}
          \t/**
          \t * @inheritdoc OpenSeaFactoryERC721
          \t */
          \tfunction mint(uint256 _optionId, address _toAddress) public override {
          \t\t// verify the access permission
          \t\trequire(address(ProxyRegistry(proxyRegistry).proxies(owner)) == msg.sender, "access denied");
          \t\t// verify option ID can be minted
          \t\trequire(canMint(_optionId), "cannot mint");
          \t\t// do the mint
          \t\tMintableERC721(nftContract).mint(_toAddress, currentTokenId[_optionId]);
          \t\t// emit an event before increasing
          \t\temit Minted(msg.sender, currentTokenId[_optionId], _toAddress);
          \t\t// increment next tokenId
          \t\tcurrentTokenId[_optionId]++;
          \t}
          \t/**
          \t * @dev Fires transfer events for each option. Used to change contract "owner"
          \t *      See: OpenSea docs and source code examples,
          \t *      https://docs.opensea.io/docs/2-custom-sale-contract-viewing-your-sale-assets-on-opensea
          \t *
          \t * @param _from transfer optionIds from
          \t * @param _to transfer optionIds to
          \t */
          \tfunction fireTransferEvents(address _from, address _to) public {
          \t\t// verify the access permission
          \t\trequire(isSenderInRole(ROLE_OS_MANAGER), "access denied");
          \t\t// fire events for each option
          \t\tfor (uint256 i = 0; i < options; i++) {
          \t\t\temit Transfer(_from, _to, i);
          \t\t}
          \t}
          \t/**
          \t * Hack to get things to work automatically on OpenSea.
          \t * Use transferFrom so the frontend doesn't have to worry about different method names.
          \t */
          \tfunction transferFrom(address _from, address _to, uint256 _tokenId) public {
          \t\t// simply delegate to `mint`
          \t\tmint(_tokenId, _to);
          \t}
          \t/**
          \t * Hack to get things to work automatically on OpenSea.
          \t * Use isApprovedForAll so the frontend doesn't have to worry about different method names.
          \t */
          \tfunction isApprovedForAll(address _owner, address _operator) public view returns (bool) {
          \t\t// true if called by contract "owner" which is the token owner itself
          \t\tif(owner == _owner && _owner == _operator) {
          \t\t\treturn true;
          \t\t}
          \t\t// lookup the registry
          \t\treturn owner == _owner && address(ProxyRegistry(proxyRegistry).proxies(_owner)) == _operator;
          \t}
          \t/**
          \t * Hack to get things to work automatically on OpenSea.
          \t * Use isApprovedForAll so the frontend doesn't have to worry about different method names.
          \t */
          \tfunction ownerOf(uint256 _tokenId) public view returns (address _owner) {
          \t\t// return smart contract "owner"
          \t\treturn owner;
          \t}
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.7;
          /**
           * @title ERC-165 Standard Interface Detection
           *
           * @dev Interface of the ERC165 standard, as defined in the
           *       https://eips.ethereum.org/EIPS/eip-165[EIP].
           *
           * @dev Implementers can declare support of contract interfaces,
           *      which can then be queried by others.
           *
           * @author Christian Reitwießner, Nick Johnson, Fabian Vogelsteller, Jordi Baylina, Konrad Feldmeier, William Entriken
           */
          interface ERC165 {
          \t/**
          \t * @notice Query if a contract implements an interface
          \t *
          \t * @dev Interface identification is specified in ERC-165.
          \t *      This function uses less than 30,000 gas.
          \t *
          \t * @param interfaceID The interface identifier, as specified in ERC-165
          \t * @return `true` if the contract implements `interfaceID` and
          \t *      `interfaceID` is not 0xffffffff, `false` otherwise
          \t */
          \tfunction supportsInterface(bytes4 interfaceID) external view returns (bool);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.7;
          import "./ERC165Spec.sol";
          /**
           * @title ERC-721 Non-Fungible Token Standard
           *
           * @notice See https://eips.ethereum.org/EIPS/eip-721
           *
           * @dev Solidity issue #3412: The ERC721 interfaces include explicit mutability guarantees for each function.
           *      Mutability guarantees are, in order weak to strong: payable, implicit nonpayable, view, and pure.
           *      Implementation MUST meet the mutability guarantee in this interface and MAY meet a stronger guarantee.
           *      For example, a payable function in this interface may be implemented as nonpayable
           *      (no state mutability specified) in implementing contract.
           *      It is expected a later Solidity release will allow stricter contract to inherit from this interface,
           *      but current workaround is that we edit this interface to add stricter mutability before inheriting:
           *      we have removed all "payable" modifiers.
           *
           * @dev The ERC-165 identifier for this interface is 0x80ac58cd.
           *
           * @author William Entriken, Dieter Shirley, Jacob Evans, Nastassia Sachs
           */
          interface ERC721 is ERC165 {
          \t/// @dev This emits when ownership of any NFT changes by any mechanism.
          \t///  This event emits when NFTs are created (`from` == 0) and destroyed
          \t///  (`to` == 0). Exception: during contract creation, any number of NFTs
          \t///  may be created and assigned without emitting Transfer. At the time of
          \t///  any transfer, the approved address for that NFT (if any) is reset to none.
          \tevent Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
          \t/// @dev This emits when the approved address for an NFT is changed or
          \t///  reaffirmed. The zero address indicates there is no approved address.
          \t///  When a Transfer event emits, this also indicates that the approved
          \t///  address for that NFT (if any) is reset to none.
          \tevent Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
          \t/// @dev This emits when an operator is enabled or disabled for an owner.
          \t///  The operator can manage all NFTs of the owner.
          \tevent ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
          \t/// @notice Count all NFTs assigned to an owner
          \t/// @dev NFTs assigned to the zero address are considered invalid, and this
          \t///  function throws for queries about the zero address.
          \t/// @param _owner An address for whom to query the balance
          \t/// @return The number of NFTs owned by `_owner`, possibly zero
          \tfunction balanceOf(address _owner) external view returns (uint256);
          \t/// @notice Find the owner of an NFT
          \t/// @dev NFTs assigned to zero address are considered invalid, and queries
          \t///  about them do throw.
          \t/// @param _tokenId The identifier for an NFT
          \t/// @return The address of the owner of the NFT
          \tfunction ownerOf(uint256 _tokenId) external view returns (address);
          \t/// @notice Transfers the ownership of an NFT from one address to another address
          \t/// @dev Throws unless `msg.sender` is the current owner, an authorized
          \t///  operator, or the approved address for this NFT. Throws if `_from` is
          \t///  not the current owner. Throws if `_to` is the zero address. Throws if
          \t///  `_tokenId` is not a valid NFT. When transfer is complete, this function
          \t///  checks if `_to` is a smart contract (code size > 0). If so, it calls
          \t///  `onERC721Received` on `_to` and throws if the return value is not
          \t///  `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
          \t/// @param _from The current owner of the NFT
          \t/// @param _to The new owner
          \t/// @param _tokenId The NFT to transfer
          \t/// @param _data Additional data with no specified format, sent in call to `_to`
          \tfunction safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external /*payable*/;
          \t/// @notice Transfers the ownership of an NFT from one address to another address
          \t/// @dev This works identically to the other function with an extra data parameter,
          \t///  except this function just sets data to "".
          \t/// @param _from The current owner of the NFT
          \t/// @param _to The new owner
          \t/// @param _tokenId The NFT to transfer
          \tfunction safeTransferFrom(address _from, address _to, uint256 _tokenId) external /*payable*/;
          \t/// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
          \t///  TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
          \t///  THEY MAY BE PERMANENTLY LOST
          \t/// @dev Throws unless `msg.sender` is the current owner, an authorized
          \t///  operator, or the approved address for this NFT. Throws if `_from` is
          \t///  not the current owner. Throws if `_to` is the zero address. Throws if
          \t///  `_tokenId` is not a valid NFT.
          \t/// @param _from The current owner of the NFT
          \t/// @param _to The new owner
          \t/// @param _tokenId The NFT to transfer
          \tfunction transferFrom(address _from, address _to, uint256 _tokenId) external /*payable*/;
          \t/// @notice Change or reaffirm the approved address for an NFT
          \t/// @dev The zero address indicates there is no approved address.
          \t///  Throws unless `msg.sender` is the current NFT owner, or an authorized
          \t///  operator of the current owner.
          \t/// @param _approved The new approved NFT controller
          \t/// @param _tokenId The NFT to approve
          \tfunction approve(address _approved, uint256 _tokenId) external /*payable*/;
          \t/// @notice Enable or disable approval for a third party ("operator") to manage
          \t///  all of `msg.sender`'s assets
          \t/// @dev Emits the ApprovalForAll event. The contract MUST allow
          \t///  multiple operators per owner.
          \t/// @param _operator Address to add to the set of authorized operators
          \t/// @param _approved True if the operator is approved, false to revoke approval
          \tfunction setApprovalForAll(address _operator, bool _approved) external;
          \t/// @notice Get the approved address for a single NFT
          \t/// @dev Throws if `_tokenId` is not a valid NFT.
          \t/// @param _tokenId The NFT to find the approved address for
          \t/// @return The approved address for this NFT, or the zero address if there is none
          \tfunction getApproved(uint256 _tokenId) external view returns (address);
          \t/// @notice Query if an address is an authorized operator for another address
          \t/// @param _owner The address that owns the NFTs
          \t/// @param _operator The address that acts on behalf of the owner
          \t/// @return True if `_operator` is an approved operator for `_owner`, false otherwise
          \tfunction isApprovedForAll(address _owner, address _operator) external view returns (bool);
          }
          /// @dev Note: the ERC-165 identifier for this interface is 0x150b7a02.
          interface ERC721TokenReceiver {
          \t/// @notice Handle the receipt of an NFT
          \t/// @dev The ERC721 smart contract calls this function on the recipient
          \t///  after a `transfer`. This function MAY throw to revert and reject the
          \t///  transfer. Return of other than the magic value MUST result in the
          \t///  transaction being reverted.
          \t///  Note: the contract address is always the message sender.
          \t/// @param _operator The address which called `safeTransferFrom` function
          \t/// @param _from The address which previously owned the token
          \t/// @param _tokenId The NFT identifier which is being transferred
          \t/// @param _data Additional data with no specified format
          \t/// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
          \t///  unless throwing
          \tfunction onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) external returns(bytes4);
          }
          /**
           * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
           *
           * @notice See https://eips.ethereum.org/EIPS/eip-721
           *
           * @dev The ERC-165 identifier for this interface is 0x5b5e139f.
           *
           * @author William Entriken, Dieter Shirley, Jacob Evans, Nastassia Sachs
           */
          interface ERC721Metadata is ERC721 {
          \t/// @notice A descriptive name for a collection of NFTs in this contract
          \tfunction name() external view returns (string memory _name);
          \t/// @notice An abbreviated name for NFTs in this contract
          \tfunction symbol() external view returns (string memory _symbol);
          \t/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
          \t/// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
          \t///  3986. The URI may point to a JSON file that conforms to the "ERC721
          \t///  Metadata JSON Schema".
          \tfunction tokenURI(uint256 _tokenId) external view returns (string memory);
          }
          /**
           * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
           *
           * @notice See https://eips.ethereum.org/EIPS/eip-721
           *
           * @dev The ERC-165 identifier for this interface is 0x780e9d63.
           *
           * @author William Entriken, Dieter Shirley, Jacob Evans, Nastassia Sachs
           */
          interface ERC721Enumerable is ERC721 {
          \t/// @notice Count NFTs tracked by this contract
          \t/// @return A count of valid NFTs tracked by this contract, where each one of
          \t///  them has an assigned and queryable owner not equal to the zero address
          \tfunction totalSupply() external view returns (uint256);
          \t/// @notice Enumerate valid NFTs
          \t/// @dev Throws if `_index` >= `totalSupply()`.
          \t/// @param _index A counter less than `totalSupply()`
          \t/// @return The token identifier for the `_index`th NFT,
          \t///  (sort order not specified)
          \tfunction tokenByIndex(uint256 _index) external view returns (uint256);
          \t/// @notice Enumerate NFTs assigned to an owner
          \t/// @dev Throws if `_index` >= `balanceOf(_owner)` or if
          \t///  `_owner` is the zero address, representing invalid NFTs.
          \t/// @param _owner An address where we are interested in NFTs owned by them
          \t/// @param _index A counter less than `balanceOf(_owner)`
          \t/// @return The token identifier for the `_index`th NFT assigned to `_owner`,
          \t///   (sort order not specified)
          \tfunction tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.7;
          /**
           * @title Alethea Mintable ERC721
           *
           * @notice Defines mint capabilities for Alethea ERC721 tokens.
           *      This interface should be treated as a definition of what mintable means for ERC721
           */
          interface MintableERC721 {
          \t/**
          \t * @notice Checks if specified token exists
          \t *
          \t * @dev Returns whether the specified token ID has an ownership
          \t *      information associated with it
          \t *
          \t * @param _tokenId ID of the token to query existence for
          \t * @return whether the token exists (true - exists, false - doesn't exist)
          \t */
          \tfunction exists(uint256 _tokenId) external view returns(bool);
          \t/**
          \t * @dev Creates new token with token ID specified
          \t *      and assigns an ownership `_to` for this token
          \t *
          \t * @dev Unsafe: doesn't execute `onERC721Received` on the receiver.
          \t *      Prefer the use of `saveMint` instead of `mint`.
          \t *
          \t * @dev Should have a restricted access handled by the implementation
          \t *
          \t * @param _to an address to mint token to
          \t * @param _tokenId ID of the token to mint
          \t */
          \tfunction mint(address _to, uint256 _tokenId) external;
          \t/**
          \t * @dev Creates new tokens starting with token ID specified
          \t *      and assigns an ownership `_to` for these tokens
          \t *
          \t * @dev Token IDs to be minted: [_tokenId, _tokenId + n)
          \t *
          \t * @dev n must be greater or equal 2: `n > 1`
          \t *
          \t * @dev Unsafe: doesn't execute `onERC721Received` on the receiver.
          \t *      Prefer the use of `saveMintBatch` instead of `mintBatch`.
          \t *
          \t * @dev Should have a restricted access handled by the implementation
          \t *
          \t * @param _to an address to mint tokens to
          \t * @param _tokenId ID of the first token to mint
          \t * @param n how many tokens to mint, sequentially increasing the _tokenId
          \t */
          \tfunction mintBatch(address _to, uint256 _tokenId, uint256 n) external;
          \t/**
          \t * @dev Creates new token with token ID specified
          \t *      and assigns an ownership `_to` for this token
          \t *
          \t * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls
          \t *      `onERC721Received` on `_to` and throws if the return value is not
          \t *      `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
          \t *
          \t * @dev Should have a restricted access handled by the implementation
          \t *
          \t * @param _to an address to mint token to
          \t * @param _tokenId ID of the token to mint
          \t */
          \tfunction safeMint(address _to, uint256 _tokenId) external;
          \t/**
          \t * @dev Creates new token with token ID specified
          \t *      and assigns an ownership `_to` for this token
          \t *
          \t * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls
          \t *      `onERC721Received` on `_to` and throws if the return value is not
          \t *      `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
          \t *
          \t * @dev Should have a restricted access handled by the implementation
          \t *
          \t * @param _to an address to mint token to
          \t * @param _tokenId ID of the token to mint
          \t * @param _data additional data with no specified format, sent in call to `_to`
          \t */
          \tfunction safeMint(address _to, uint256 _tokenId, bytes memory _data) external;
          \t/**
          \t * @dev Creates new tokens starting with token ID specified
          \t *      and assigns an ownership `_to` for these tokens
          \t *
          \t * @dev Token IDs to be minted: [_tokenId, _tokenId + n)
          \t *
          \t * @dev n must be greater or equal 2: `n > 1`
          \t *
          \t * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls
          \t *      `onERC721Received` on `_to` and throws if the return value is not
          \t *      `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
          \t *
          \t * @dev Should have a restricted access handled by the implementation
          \t *
          \t * @param _to an address to mint token to
          \t * @param _tokenId ID of the token to mint
          \t * @param n how many tokens to mint, sequentially increasing the _tokenId
          \t */
          \tfunction safeMintBatch(address _to, uint256 _tokenId, uint256 n) external;
          \t/**
          \t * @dev Creates new tokens starting with token ID specified
          \t *      and assigns an ownership `_to` for these tokens
          \t *
          \t * @dev Token IDs to be minted: [_tokenId, _tokenId + n)
          \t *
          \t * @dev n must be greater or equal 2: `n > 1`
          \t *
          \t * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls
          \t *      `onERC721Received` on `_to` and throws if the return value is not
          \t *      `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
          \t *
          \t * @dev Should have a restricted access handled by the implementation
          \t *
          \t * @param _to an address to mint token to
          \t * @param _tokenId ID of the token to mint
          \t * @param n how many tokens to mint, sequentially increasing the _tokenId
          \t * @param _data additional data with no specified format, sent in call to `_to`
          \t */
          \tfunction safeMintBatch(address _to, uint256 _tokenId, uint256 n, bytes memory _data) external;
          }
          /**
           * @title Alethea Burnable ERC721
           *
           * @notice Defines burn capabilities for Alethea ERC721 tokens.
           *      This interface should be treated as a definition of what burnable means for ERC721
           */
          interface BurnableERC721 {
          \t/**
          \t * @notice Destroys the token with token ID specified
          \t *
          \t * @dev Should be accessible publicly by token owners.
          \t *      May have a restricted access handled by the implementation
          \t *
          \t * @param _tokenId ID of the token to burn
          \t */
          \tfunction burn(uint256 _tokenId) external;
          }
          /**
           * @title With Base URI
           *
           * @notice A marker interface for the contracts having the baseURI() function
           *      or public string variable named baseURI
           *      NFT implementations like TinyERC721, or ShortERC721 are example of such smart contracts
           */
          interface WithBaseURI {
          \t/**
          \t * @dev Usually used in NFT implementations to construct ERC721Metadata.tokenURI as
          \t *      `base URI + token ID` if token URI is not set (not present in `_tokenURIs` mapping)
          \t *
          \t * @dev For example, if base URI is https://api.com/token/, then token #1
          \t *      will have an URI https://api.com/token/1
          \t */
          \tfunction baseURI() external view returns(string memory);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.7;
          /**
           * @title String Utils Library
           *
           * @dev Library for working with strings, primarily converting
           *      between strings and integer types
           */
          library StringUtils {
          \t/**
          \t * @dev Converts a string to unsigned integer using the specified `base`
          \t * @dev Throws on invalid input
          \t *      (wrong characters for a given `base`)
          \t * @dev Throws if given `base` is not supported
          \t * @param a string to convert
          \t * @param base number base, one of 2, 8, 10, 16
          \t * @return i a number representing given string
          \t */
          \tfunction atoi(string memory a, uint8 base) internal pure returns (uint256 i) {
          \t\t// check if the base is valid
          \t\trequire(base == 2 || base == 8 || base == 10 || base == 16);
          \t\t// convert string into bytes for convenient iteration
          \t\tbytes memory buf = bytes(a);
          \t\t// iterate over the string (bytes buffer)
          \t\tfor(uint256 p = 0; p < buf.length; p++) {
          \t\t\t// extract the digit
          \t\t\tuint8 digit = uint8(buf[p]) - 0x30;
          \t\t\t// if digit is greater then 10 - mind the gap
          \t\t\t// see `itoa` function for more details
          \t\t\tif(digit > 10) {
          \t\t\t\t// remove the gap
          \t\t\t\tdigit -= 7;
          \t\t\t}
          \t\t\t// check if digit meets the base
          \t\t\trequire(digit < base);
          \t\t\t// move to the next digit slot
          \t\t\ti *= base;
          \t\t\t// add digit to the result
          \t\t\ti += digit;
          \t\t}
          \t\t// return the result
          \t\treturn i;
          \t}
          \t/**
          \t * @dev Converts a integer to a string using the specified `base`
          \t * @dev Throws if given `base` is not supported
          \t * @param i integer to convert
          \t * @param base number base, one of 2, 8, 10, 16
          \t * @return a a string representing given integer
          \t */
          \tfunction itoa(uint256 i, uint8 base) internal pure returns (string memory a) {
          \t\t// check if the base is valid
          \t\trequire(base == 2 || base == 8 || base == 10 || base == 16);
          \t\t// for zero input the result is "0" string for any base
          \t\tif(i == 0) {
          \t\t\treturn "0";
          \t\t}
          \t\t// bytes buffer to put ASCII characters into
          \t\tbytes memory buf = new bytes(256);
          \t\t// position within a buffer to be used in cycle
          \t\tuint256 p = 0;
          \t\t// extract digits one by one in a cycle
          \t\twhile(i > 0) {
          \t\t\t// extract current digit
          \t\t\tuint8 digit = uint8(i % base);
          \t\t\t// convert it to an ASCII code
          \t\t\t// 0x20 is " "
          \t\t\t// 0x30-0x39 is "0"-"9"
          \t\t\t// 0x41-0x5A is "A"-"Z"
          \t\t\t// 0x61-0x7A is "a"-"z" ("A"-"Z" XOR " ")
          \t\t\tuint8 ascii = digit + 0x30;
          \t\t\t// if digit is greater then 10,
          \t\t\t// fix the 0x3A-0x40 gap of punctuation marks
          \t\t\t// (7 characters in ASCII table)
          \t\t\tif(digit >= 10) {
          \t\t\t\t// jump through the gap
          \t\t\t\tascii += 7;
          \t\t\t}
          \t\t\t// write character into the buffer
          \t\t\tbuf[p++] = bytes1(ascii);
          \t\t\t// move to the next digit
          \t\t\ti /= base;
          \t\t}
          \t\t// `p` contains real length of the buffer now,
          \t\t// allocate the resulting buffer of that size
          \t\tbytes memory result = new bytes(p);
          \t\t// copy the buffer in the reversed order
          \t\tfor(p = 0; p < result.length; p++) {
          \t\t\t// copy from the beginning of the original buffer
          \t\t\t// to the end of resulting smaller buffer
          \t\t\tresult[result.length - p - 1] = buf[p];
          \t\t}
          \t\t// construct string and return
          \t\treturn string(result);
          \t}
          \t/**
          \t * @dev Concatenates two strings `s1` and `s2`, for example, if
          \t *      `s1` == `foo` and `s2` == `bar`, the result `s` == `foobar`
          \t * @param s1 first string
          \t * @param s2 second string
          \t * @return s concatenation result s1 + s2
          \t */
          \tfunction concat(string memory s1, string memory s2) internal pure returns (string memory s) {
          \t\t// an old way of string concatenation (Solidity 0.4) is commented out
          /*
          \t\t// convert s1 into buffer 1
          \t\tbytes memory buf1 = bytes(s1);
          \t\t// convert s2 into buffer 2
          \t\tbytes memory buf2 = bytes(s2);
          \t\t// create a buffer for concatenation result
          \t\tbytes memory buf = new bytes(buf1.length + buf2.length);
          \t\t// copy buffer 1 into buffer
          \t\tfor(uint256 i = 0; i < buf1.length; i++) {
          \t\t\tbuf[i] = buf1[i];
          \t\t}
          \t\t// copy buffer 2 into buffer
          \t\tfor(uint256 j = buf1.length; j < buf2.length; j++) {
          \t\t\tbuf[j] = buf2[j - buf1.length];
          \t\t}
          \t\t// construct string and return
          \t\treturn string(buf);
          */
          \t\t// simply use built in function
          \t\treturn string(abi.encodePacked(s1, s2));
          \t}
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.7;
          /**
           * @title Access Control List
           *
           * @notice Access control smart contract provides an API to check
           *      if specific operation is permitted globally and/or
           *      if particular user has a permission to execute it.
           *
           * @notice It deals with two main entities: features and roles.
           *
           * @notice Features are designed to be used to enable/disable specific
           *      functions (public functions) of the smart contract for everyone.
           * @notice User roles are designed to restrict access to specific
           *      functions (restricted functions) of the smart contract to some users.
           *
           * @notice Terms "role", "permissions" and "set of permissions" have equal meaning
           *      in the documentation text and may be used interchangeably.
           * @notice Terms "permission", "single permission" implies only one permission bit set.
           *
           * @notice Access manager is a special role which allows to grant/revoke other roles.
           *      Access managers can only grant/revoke permissions which they have themselves.
           *      As an example, access manager with no other roles set can only grant/revoke its own
           *      access manager permission and nothing else.
           *
           * @notice Access manager permission should be treated carefully, as a super admin permission:
           *      Access manager with even no other permission can interfere with another account by
           *      granting own access manager permission to it and effectively creating more powerful
           *      permission set than its own.
           *
           * @dev Both current and OpenZeppelin AccessControl implementations feature a similar API
           *      to check/know "who is allowed to do this thing".
           * @dev Zeppelin implementation is more flexible:
           *      - it allows setting unlimited number of roles, while current is limited to 256 different roles
           *      - it allows setting an admin for each role, while current allows having only one global admin
           * @dev Current implementation is more lightweight:
           *      - it uses only 1 bit per role, while Zeppelin uses 256 bits
           *      - it allows setting up to 256 roles at once, in a single transaction, while Zeppelin allows
           *        setting only one role in a single transaction
           *
           * @dev This smart contract is designed to be inherited by other
           *      smart contracts which require access control management capabilities.
           *
           * @dev Access manager permission has a bit 255 set.
           *      This bit must not be used by inheriting contracts for any other permissions/features.
           */
          contract AccessControl {
          \t/**
          \t * @notice Access manager is responsible for assigning the roles to users,
          \t *      enabling/disabling global features of the smart contract
          \t * @notice Access manager can add, remove and update user roles,
          \t *      remove and update global features
          \t *
          \t * @dev Role ROLE_ACCESS_MANAGER allows modifying user roles and global features
          \t * @dev Role ROLE_ACCESS_MANAGER has single bit at position 255 enabled
          \t */
          \tuint256 public constant ROLE_ACCESS_MANAGER = 0x8000000000000000000000000000000000000000000000000000000000000000;
          \t/**
          \t * @dev Bitmask representing all the possible permissions (super admin role)
          \t * @dev Has all the bits are enabled (2^256 - 1 value)
          \t */
          \tuint256 private constant FULL_PRIVILEGES_MASK = type(uint256).max; // before 0.8.0: uint256(-1) overflows to 0xFFFF...
          \t/**
          \t * @notice Privileged addresses with defined roles/permissions
          \t * @notice In the context of ERC20/ERC721 tokens these can be permissions to
          \t *      allow minting or burning tokens, transferring on behalf and so on
          \t *
          \t * @dev Maps user address to the permissions bitmask (role), where each bit
          \t *      represents a permission
          \t * @dev Bitmask 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
          \t *      represents all possible permissions
          \t * @dev 'This' address mapping represents global features of the smart contract
          \t */
          \tmapping(address => uint256) public userRoles;
          \t/**
          \t * @dev Fired in updateRole() and updateFeatures()
          \t *
          \t * @param _by operator which called the function
          \t * @param _to address which was granted/revoked permissions
          \t * @param _requested permissions requested
          \t * @param _actual permissions effectively set
          \t */
          \tevent RoleUpdated(address indexed _by, address indexed _to, uint256 _requested, uint256 _actual);
          \t/**
          \t * @notice Creates an access control instance,
          \t *      setting contract creator to have full privileges
          \t */
          \tconstructor() {
          \t\t// contract creator has full privileges
          \t\tuserRoles[msg.sender] = FULL_PRIVILEGES_MASK;
          \t}
          \t/**
          \t * @notice Retrieves globally set of features enabled
          \t *
          \t * @dev Effectively reads userRoles role for the contract itself
          \t *
          \t * @return 256-bit bitmask of the features enabled
          \t */
          \tfunction features() public view returns(uint256) {
          \t\t// features are stored in 'this' address  mapping of `userRoles` structure
          \t\treturn userRoles[address(this)];
          \t}
          \t/**
          \t * @notice Updates set of the globally enabled features (`features`),
          \t *      taking into account sender's permissions
          \t *
          \t * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
          \t * @dev Function is left for backward compatibility with older versions
          \t *
          \t * @param _mask bitmask representing a set of features to enable/disable
          \t */
          \tfunction updateFeatures(uint256 _mask) public {
          \t\t// delegate call to `updateRole`
          \t\tupdateRole(address(this), _mask);
          \t}
          \t/**
          \t * @notice Updates set of permissions (role) for a given user,
          \t *      taking into account sender's permissions.
          \t *
          \t * @dev Setting role to zero is equivalent to removing an all permissions
          \t * @dev Setting role to `FULL_PRIVILEGES_MASK` is equivalent to
          \t *      copying senders' permissions (role) to the user
          \t * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
          \t *
          \t * @param operator address of a user to alter permissions for or zero
          \t *      to alter global features of the smart contract
          \t * @param role bitmask representing a set of permissions to
          \t *      enable/disable for a user specified
          \t */
          \tfunction updateRole(address operator, uint256 role) public {
          \t\t// caller must have a permission to update user roles
          \t\trequire(isSenderInRole(ROLE_ACCESS_MANAGER), "access denied");
          \t\t// evaluate the role and reassign it
          \t\tuserRoles[operator] = evaluateBy(msg.sender, userRoles[operator], role);
          \t\t// fire an event
          \t\temit RoleUpdated(msg.sender, operator, role, userRoles[operator]);
          \t}
          \t/**
          \t * @notice Determines the permission bitmask an operator can set on the
          \t *      target permission set
          \t * @notice Used to calculate the permission bitmask to be set when requested
          \t *     in `updateRole` and `updateFeatures` functions
          \t *
          \t * @dev Calculated based on:
          \t *      1) operator's own permission set read from userRoles[operator]
          \t *      2) target permission set - what is already set on the target
          \t *      3) desired permission set - what do we want set target to
          \t *
          \t * @dev Corner cases:
          \t *      1) Operator is super admin and its permission set is `FULL_PRIVILEGES_MASK`:
          \t *        `desired` bitset is returned regardless of the `target` permission set value
          \t *        (what operator sets is what they get)
          \t *      2) Operator with no permissions (zero bitset):
          \t *        `target` bitset is returned regardless of the `desired` value
          \t *        (operator has no authority and cannot modify anything)
          \t *
          \t * @dev Example:
          \t *      Consider an operator with the permissions bitmask     00001111
          \t *      is about to modify the target permission set          01010101
          \t *      Operator wants to set that permission set to          00110011
          \t *      Based on their role, an operator has the permissions
          \t *      to update only lowest 4 bits on the target, meaning that
          \t *      high 4 bits of the target set in this example is left
          \t *      unchanged and low 4 bits get changed as desired:      01010011
          \t *
          \t * @param operator address of the contract operator which is about to set the permissions
          \t * @param target input set of permissions to operator is going to modify
          \t * @param desired desired set of permissions operator would like to set
          \t * @return resulting set of permissions given operator will set
          \t */
          \tfunction evaluateBy(address operator, uint256 target, uint256 desired) public view returns(uint256) {
          \t\t// read operator's permissions
          \t\tuint256 p = userRoles[operator];
          \t\t// taking into account operator's permissions,
          \t\t// 1) enable the permissions desired on the `target`
          \t\ttarget |= p & desired;
          \t\t// 2) disable the permissions desired on the `target`
          \t\ttarget &= FULL_PRIVILEGES_MASK ^ (p & (FULL_PRIVILEGES_MASK ^ desired));
          \t\t// return calculated result
          \t\treturn target;
          \t}
          \t/**
          \t * @notice Checks if requested set of features is enabled globally on the contract
          \t *
          \t * @param required set of features to check against
          \t * @return true if all the features requested are enabled, false otherwise
          \t */
          \tfunction isFeatureEnabled(uint256 required) public view returns(bool) {
          \t\t// delegate call to `__hasRole`, passing `features` property
          \t\treturn __hasRole(features(), required);
          \t}
          \t/**
          \t * @notice Checks if transaction sender `msg.sender` has all the permissions required
          \t *
          \t * @param required set of permissions (role) to check against
          \t * @return true if all the permissions requested are enabled, false otherwise
          \t */
          \tfunction isSenderInRole(uint256 required) public view returns(bool) {
          \t\t// delegate call to `isOperatorInRole`, passing transaction sender
          \t\treturn isOperatorInRole(msg.sender, required);
          \t}
          \t/**
          \t * @notice Checks if operator has all the permissions (role) required
          \t *
          \t * @param operator address of the user to check role for
          \t * @param required set of permissions (role) to check
          \t * @return true if all the permissions requested are enabled, false otherwise
          \t */
          \tfunction isOperatorInRole(address operator, uint256 required) public view returns(bool) {
          \t\t// delegate call to `__hasRole`, passing operator's permissions (role)
          \t\treturn __hasRole(userRoles[operator], required);
          \t}
          \t/**
          \t * @dev Checks if role `actual` contains all the permissions required `required`
          \t *
          \t * @param actual existent role
          \t * @param required required role
          \t * @return true if actual has required role (all permissions), false otherwise
          \t */
          \tfunction __hasRole(uint256 actual, uint256 required) internal pure returns(bool) {
          \t\t// check the bitmask for the role required and return the result
          \t\treturn actual & required == required;
          \t}
          }
          

          File 4 of 5: WyvernProxyRegistry
          pragma solidity ^0.4.13;
          
          contract Ownable {
            address public owner;
          
          
            event OwnershipRenounced(address indexed previousOwner);
            event OwnershipTransferred(
              address indexed previousOwner,
              address indexed newOwner
            );
          
          
            /**
             * @dev The Ownable constructor sets the original `owner` of the contract to the sender
             * account.
             */
            constructor() public {
              owner = msg.sender;
            }
          
            /**
             * @dev Throws if called by any account other than the owner.
             */
            modifier onlyOwner() {
              require(msg.sender == owner);
              _;
            }
          
            /**
             * @dev Allows the current owner to transfer control of the contract to a newOwner.
             * @param newOwner The address to transfer ownership to.
             */
            function transferOwnership(address newOwner) public onlyOwner {
              require(newOwner != address(0));
              emit OwnershipTransferred(owner, newOwner);
              owner = newOwner;
            }
          
            /**
             * @dev Allows the current owner to relinquish control of the contract.
             */
            function renounceOwnership() public onlyOwner {
              emit OwnershipRenounced(owner);
              owner = address(0);
            }
          }
          
          contract ERC20Basic {
            function totalSupply() public view returns (uint256);
            function balanceOf(address who) public view returns (uint256);
            function transfer(address to, uint256 value) public returns (bool);
            event Transfer(address indexed from, address indexed to, uint256 value);
          }
          
          contract ERC20 is ERC20Basic {
            function allowance(address owner, address spender)
              public view returns (uint256);
          
            function transferFrom(address from, address to, uint256 value)
              public returns (bool);
          
            function approve(address spender, uint256 value) public returns (bool);
            event Approval(
              address indexed owner,
              address indexed spender,
              uint256 value
            );
          }
          
          contract TokenRecipient {
              event ReceivedEther(address indexed sender, uint amount);
              event ReceivedTokens(address indexed from, uint256 value, address indexed token, bytes extraData);
          
              /**
               * @dev Receive tokens and generate a log event
               * @param from Address from which to transfer tokens
               * @param value Amount of tokens to transfer
               * @param token Address of token
               * @param extraData Additional data to log
               */
              function receiveApproval(address from, uint256 value, address token, bytes extraData) public {
                  ERC20 t = ERC20(token);
                  require(t.transferFrom(from, this, value));
                  emit ReceivedTokens(from, value, token, extraData);
              }
          
              /**
               * @dev Receive Ether and generate a log event
               */
              function () payable public {
                  emit ReceivedEther(msg.sender, msg.value);
              }
          }
          
          contract ProxyRegistry is Ownable {
          
              /* DelegateProxy implementation contract. Must be initialized. */
              address public delegateProxyImplementation;
          
              /* Authenticated proxies by user. */
              mapping(address => OwnableDelegateProxy) public proxies;
          
              /* Contracts pending access. */
              mapping(address => uint) public pending;
          
              /* Contracts allowed to call those proxies. */
              mapping(address => bool) public contracts;
          
              /* Delay period for adding an authenticated contract.
                 This mitigates a particular class of potential attack on the Wyvern DAO (which owns this registry) - if at any point the value of assets held by proxy contracts exceeded the value of half the WYV supply (votes in the DAO),
                 a malicious but rational attacker could buy half the Wyvern and grant themselves access to all the proxy contracts. A delay period renders this attack nonthreatening - given two weeks, if that happened, users would have
                 plenty of time to notice and transfer their assets.
              */
              uint public DELAY_PERIOD = 2 weeks;
          
              /**
               * Start the process to enable access for specified contract. Subject to delay period.
               *
               * @dev ProxyRegistry owner only
               * @param addr Address to which to grant permissions
               */
              function startGrantAuthentication (address addr)
                  public
                  onlyOwner
              {
                  require(!contracts[addr] && pending[addr] == 0);
                  pending[addr] = now;
              }
          
              /**
               * End the process to nable access for specified contract after delay period has passed.
               *
               * @dev ProxyRegistry owner only
               * @param addr Address to which to grant permissions
               */
              function endGrantAuthentication (address addr)
                  public
                  onlyOwner
              {
                  require(!contracts[addr] && pending[addr] != 0 && ((pending[addr] + DELAY_PERIOD) < now));
                  pending[addr] = 0;
                  contracts[addr] = true;
              }
          
              /**
               * Revoke access for specified contract. Can be done instantly.
               *
               * @dev ProxyRegistry owner only
               * @param addr Address of which to revoke permissions
               */    
              function revokeAuthentication (address addr)
                  public
                  onlyOwner
              {
                  contracts[addr] = false;
              }
          
              /**
               * Register a proxy contract with this registry
               *
               * @dev Must be called by the user which the proxy is for, creates a new AuthenticatedProxy
               * @return New AuthenticatedProxy contract
               */
              function registerProxy()
                  public
                  returns (OwnableDelegateProxy proxy)
              {
                  require(proxies[msg.sender] == address(0));
                  proxy = new OwnableDelegateProxy(msg.sender, delegateProxyImplementation, abi.encodeWithSignature("initialize(address,address)", msg.sender, address(this)));
                  proxies[msg.sender] = proxy;
                  return proxy;
              }
          
          }
          
          contract WyvernProxyRegistry is ProxyRegistry {
          
              string public constant name = "Project Wyvern Proxy Registry";
          
              /* Whether the initial auth address has been set. */
              bool public initialAddressSet = false;
          
              constructor ()
                  public
              {
                  delegateProxyImplementation = new AuthenticatedProxy();
              }
          
              /** 
               * Grant authentication to the initial Exchange protocol contract
               *
               * @dev No delay, can only be called once - after that the standard registry process with a delay must be used
               * @param authAddress Address of the contract to grant authentication
               */
              function grantInitialAuthentication (address authAddress)
                  onlyOwner
                  public
              {
                  require(!initialAddressSet);
                  initialAddressSet = true;
                  contracts[authAddress] = true;
              }
          
          }
          
          contract OwnedUpgradeabilityStorage {
          
            // Current implementation
            address internal _implementation;
          
            // Owner of the contract
            address private _upgradeabilityOwner;
          
            /**
             * @dev Tells the address of the owner
             * @return the address of the owner
             */
            function upgradeabilityOwner() public view returns (address) {
              return _upgradeabilityOwner;
            }
          
            /**
             * @dev Sets the address of the owner
             */
            function setUpgradeabilityOwner(address newUpgradeabilityOwner) internal {
              _upgradeabilityOwner = newUpgradeabilityOwner;
            }
          
            /**
            * @dev Tells the address of the current implementation
            * @return address of the current implementation
            */
            function implementation() public view returns (address) {
              return _implementation;
            }
          
            /**
            * @dev Tells the proxy type (EIP 897)
            * @return Proxy type, 2 for forwarding proxy
            */
            function proxyType() public pure returns (uint256 proxyTypeId) {
              return 2;
            }
          }
          
          contract AuthenticatedProxy is TokenRecipient, OwnedUpgradeabilityStorage {
          
              /* Whether initialized. */
              bool initialized = false;
          
              /* Address which owns this proxy. */
              address public user;
          
              /* Associated registry with contract authentication information. */
              ProxyRegistry public registry;
          
              /* Whether access has been revoked. */
              bool public revoked;
          
              /* Delegate call could be used to atomically transfer multiple assets owned by the proxy contract with one order. */
              enum HowToCall { Call, DelegateCall }
          
              /* Event fired when the proxy access is revoked or unrevoked. */
              event Revoked(bool revoked);
          
              /**
               * Initialize an AuthenticatedProxy
               *
               * @param addrUser Address of user on whose behalf this proxy will act
               * @param addrRegistry Address of ProxyRegistry contract which will manage this proxy
               */
              function initialize (address addrUser, ProxyRegistry addrRegistry)
                  public
              {
                  require(!initialized);
                  initialized = true;
                  user = addrUser;
                  registry = addrRegistry;
              }
          
              /**
               * Set the revoked flag (allows a user to revoke ProxyRegistry access)
               *
               * @dev Can be called by the user only
               * @param revoke Whether or not to revoke access
               */
              function setRevoke(bool revoke)
                  public
              {
                  require(msg.sender == user);
                  revoked = revoke;
                  emit Revoked(revoke);
              }
          
              /**
               * Execute a message call from the proxy contract
               *
               * @dev Can be called by the user, or by a contract authorized by the registry as long as the user has not revoked access
               * @param dest Address to which the call will be sent
               * @param howToCall Which kind of call to make
               * @param calldata Calldata to send
               * @return Result of the call (success or failure)
               */
              function proxy(address dest, HowToCall howToCall, bytes calldata)
                  public
                  returns (bool result)
              {
                  require(msg.sender == user || (!revoked && registry.contracts(msg.sender)));
                  if (howToCall == HowToCall.Call) {
                      result = dest.call(calldata);
                  } else if (howToCall == HowToCall.DelegateCall) {
                      result = dest.delegatecall(calldata);
                  }
                  return result;
              }
          
              /**
               * Execute a message call and assert success
               * 
               * @dev Same functionality as `proxy`, just asserts the return value
               * @param dest Address to which the call will be sent
               * @param howToCall What kind of call to make
               * @param calldata Calldata to send
               */
              function proxyAssert(address dest, HowToCall howToCall, bytes calldata)
                  public
              {
                  require(proxy(dest, howToCall, calldata));
              }
          
          }
          
          contract Proxy {
          
            /**
            * @dev Tells the address of the implementation where every call will be delegated.
            * @return address of the implementation to which it will be delegated
            */
            function implementation() public view returns (address);
          
            /**
            * @dev Tells the type of proxy (EIP 897)
            * @return Type of proxy, 2 for upgradeable proxy
            */
            function proxyType() public pure returns (uint256 proxyTypeId);
          
            /**
            * @dev Fallback function allowing to perform a delegatecall to the given implementation.
            * This function will return whatever the implementation call returns
            */
            function () payable public {
              address _impl = implementation();
              require(_impl != address(0));
          
              assembly {
                let ptr := mload(0x40)
                calldatacopy(ptr, 0, calldatasize)
                let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0)
                let size := returndatasize
                returndatacopy(ptr, 0, size)
          
                switch result
                case 0 { revert(ptr, size) }
                default { return(ptr, size) }
              }
            }
          }
          
          contract OwnedUpgradeabilityProxy is Proxy, OwnedUpgradeabilityStorage {
            /**
            * @dev Event to show ownership has been transferred
            * @param previousOwner representing the address of the previous owner
            * @param newOwner representing the address of the new owner
            */
            event ProxyOwnershipTransferred(address previousOwner, address newOwner);
          
            /**
            * @dev This event will be emitted every time the implementation gets upgraded
            * @param implementation representing the address of the upgraded implementation
            */
            event Upgraded(address indexed implementation);
          
            /**
            * @dev Upgrades the implementation address
            * @param implementation representing the address of the new implementation to be set
            */
            function _upgradeTo(address implementation) internal {
              require(_implementation != implementation);
              _implementation = implementation;
              emit Upgraded(implementation);
            }
          
            /**
            * @dev Throws if called by any account other than the owner.
            */
            modifier onlyProxyOwner() {
              require(msg.sender == proxyOwner());
              _;
            }
          
            /**
             * @dev Tells the address of the proxy owner
             * @return the address of the proxy owner
             */
            function proxyOwner() public view returns (address) {
              return upgradeabilityOwner();
            }
          
            /**
             * @dev Allows the current owner to transfer control of the contract to a newOwner.
             * @param newOwner The address to transfer ownership to.
             */
            function transferProxyOwnership(address newOwner) public onlyProxyOwner {
              require(newOwner != address(0));
              emit ProxyOwnershipTransferred(proxyOwner(), newOwner);
              setUpgradeabilityOwner(newOwner);
            }
          
            /**
             * @dev Allows the upgradeability owner to upgrade the current implementation of the proxy.
             * @param implementation representing the address of the new implementation to be set.
             */
            function upgradeTo(address implementation) public onlyProxyOwner {
              _upgradeTo(implementation);
            }
          
            /**
             * @dev Allows the upgradeability owner to upgrade the current implementation of the proxy
             * and delegatecall the new implementation for initialization.
             * @param implementation representing the address of the new implementation to be set.
             * @param data represents the msg.data to bet sent in the low level call. This parameter may include the function
             * signature of the implementation to be called with the needed payload
             */
            function upgradeToAndCall(address implementation, bytes data) payable public onlyProxyOwner {
              upgradeTo(implementation);
              require(address(this).delegatecall(data));
            }
          }
          
          contract OwnableDelegateProxy is OwnedUpgradeabilityProxy {
          
              constructor(address owner, address initialImplementation, bytes calldata)
                  public
              {
                  setUpgradeabilityOwner(owner);
                  _upgradeTo(initialImplementation);
                  require(initialImplementation.delegatecall(calldata));
              }
          
          }

          File 5 of 5: AuthenticatedProxy
          pragma solidity ^0.4.13;
          
          contract Ownable {
            address public owner;
          
          
            event OwnershipRenounced(address indexed previousOwner);
            event OwnershipTransferred(
              address indexed previousOwner,
              address indexed newOwner
            );
          
          
            /**
             * @dev The Ownable constructor sets the original `owner` of the contract to the sender
             * account.
             */
            constructor() public {
              owner = msg.sender;
            }
          
            /**
             * @dev Throws if called by any account other than the owner.
             */
            modifier onlyOwner() {
              require(msg.sender == owner);
              _;
            }
          
            /**
             * @dev Allows the current owner to transfer control of the contract to a newOwner.
             * @param newOwner The address to transfer ownership to.
             */
            function transferOwnership(address newOwner) public onlyOwner {
              require(newOwner != address(0));
              emit OwnershipTransferred(owner, newOwner);
              owner = newOwner;
            }
          
            /**
             * @dev Allows the current owner to relinquish control of the contract.
             */
            function renounceOwnership() public onlyOwner {
              emit OwnershipRenounced(owner);
              owner = address(0);
            }
          }
          
          contract ERC20Basic {
            function totalSupply() public view returns (uint256);
            function balanceOf(address who) public view returns (uint256);
            function transfer(address to, uint256 value) public returns (bool);
            event Transfer(address indexed from, address indexed to, uint256 value);
          }
          
          contract ERC20 is ERC20Basic {
            function allowance(address owner, address spender)
              public view returns (uint256);
          
            function transferFrom(address from, address to, uint256 value)
              public returns (bool);
          
            function approve(address spender, uint256 value) public returns (bool);
            event Approval(
              address indexed owner,
              address indexed spender,
              uint256 value
            );
          }
          
          contract ProxyRegistry is Ownable {
          
              /* DelegateProxy implementation contract. Must be initialized. */
              address public delegateProxyImplementation;
          
              /* Authenticated proxies by user. */
              mapping(address => OwnableDelegateProxy) public proxies;
          
              /* Contracts pending access. */
              mapping(address => uint) public pending;
          
              /* Contracts allowed to call those proxies. */
              mapping(address => bool) public contracts;
          
              /* Delay period for adding an authenticated contract.
                 This mitigates a particular class of potential attack on the Wyvern DAO (which owns this registry) - if at any point the value of assets held by proxy contracts exceeded the value of half the WYV supply (votes in the DAO),
                 a malicious but rational attacker could buy half the Wyvern and grant themselves access to all the proxy contracts. A delay period renders this attack nonthreatening - given two weeks, if that happened, users would have
                 plenty of time to notice and transfer their assets.
              */
              uint public DELAY_PERIOD = 2 weeks;
          
              /**
               * Start the process to enable access for specified contract. Subject to delay period.
               *
               * @dev ProxyRegistry owner only
               * @param addr Address to which to grant permissions
               */
              function startGrantAuthentication (address addr)
                  public
                  onlyOwner
              {
                  require(!contracts[addr] && pending[addr] == 0);
                  pending[addr] = now;
              }
          
              /**
               * End the process to nable access for specified contract after delay period has passed.
               *
               * @dev ProxyRegistry owner only
               * @param addr Address to which to grant permissions
               */
              function endGrantAuthentication (address addr)
                  public
                  onlyOwner
              {
                  require(!contracts[addr] && pending[addr] != 0 && ((pending[addr] + DELAY_PERIOD) < now));
                  pending[addr] = 0;
                  contracts[addr] = true;
              }
          
              /**
               * Revoke access for specified contract. Can be done instantly.
               *
               * @dev ProxyRegistry owner only
               * @param addr Address of which to revoke permissions
               */    
              function revokeAuthentication (address addr)
                  public
                  onlyOwner
              {
                  contracts[addr] = false;
              }
          
              /**
               * Register a proxy contract with this registry
               *
               * @dev Must be called by the user which the proxy is for, creates a new AuthenticatedProxy
               * @return New AuthenticatedProxy contract
               */
              function registerProxy()
                  public
                  returns (OwnableDelegateProxy proxy)
              {
                  require(proxies[msg.sender] == address(0));
                  proxy = new OwnableDelegateProxy(msg.sender, delegateProxyImplementation, abi.encodeWithSignature("initialize(address,address)", msg.sender, address(this)));
                  proxies[msg.sender] = proxy;
                  return proxy;
              }
          
          }
          
          contract TokenRecipient {
              event ReceivedEther(address indexed sender, uint amount);
              event ReceivedTokens(address indexed from, uint256 value, address indexed token, bytes extraData);
          
              /**
               * @dev Receive tokens and generate a log event
               * @param from Address from which to transfer tokens
               * @param value Amount of tokens to transfer
               * @param token Address of token
               * @param extraData Additional data to log
               */
              function receiveApproval(address from, uint256 value, address token, bytes extraData) public {
                  ERC20 t = ERC20(token);
                  require(t.transferFrom(from, this, value));
                  emit ReceivedTokens(from, value, token, extraData);
              }
          
              /**
               * @dev Receive Ether and generate a log event
               */
              function () payable public {
                  emit ReceivedEther(msg.sender, msg.value);
              }
          }
          
          contract OwnedUpgradeabilityStorage {
          
            // Current implementation
            address internal _implementation;
          
            // Owner of the contract
            address private _upgradeabilityOwner;
          
            /**
             * @dev Tells the address of the owner
             * @return the address of the owner
             */
            function upgradeabilityOwner() public view returns (address) {
              return _upgradeabilityOwner;
            }
          
            /**
             * @dev Sets the address of the owner
             */
            function setUpgradeabilityOwner(address newUpgradeabilityOwner) internal {
              _upgradeabilityOwner = newUpgradeabilityOwner;
            }
          
            /**
            * @dev Tells the address of the current implementation
            * @return address of the current implementation
            */
            function implementation() public view returns (address) {
              return _implementation;
            }
          
            /**
            * @dev Tells the proxy type (EIP 897)
            * @return Proxy type, 2 for forwarding proxy
            */
            function proxyType() public pure returns (uint256 proxyTypeId) {
              return 2;
            }
          }
          
          contract AuthenticatedProxy is TokenRecipient, OwnedUpgradeabilityStorage {
          
              /* Whether initialized. */
              bool initialized = false;
          
              /* Address which owns this proxy. */
              address public user;
          
              /* Associated registry with contract authentication information. */
              ProxyRegistry public registry;
          
              /* Whether access has been revoked. */
              bool public revoked;
          
              /* Delegate call could be used to atomically transfer multiple assets owned by the proxy contract with one order. */
              enum HowToCall { Call, DelegateCall }
          
              /* Event fired when the proxy access is revoked or unrevoked. */
              event Revoked(bool revoked);
          
              /**
               * Initialize an AuthenticatedProxy
               *
               * @param addrUser Address of user on whose behalf this proxy will act
               * @param addrRegistry Address of ProxyRegistry contract which will manage this proxy
               */
              function initialize (address addrUser, ProxyRegistry addrRegistry)
                  public
              {
                  require(!initialized);
                  initialized = true;
                  user = addrUser;
                  registry = addrRegistry;
              }
          
              /**
               * Set the revoked flag (allows a user to revoke ProxyRegistry access)
               *
               * @dev Can be called by the user only
               * @param revoke Whether or not to revoke access
               */
              function setRevoke(bool revoke)
                  public
              {
                  require(msg.sender == user);
                  revoked = revoke;
                  emit Revoked(revoke);
              }
          
              /**
               * Execute a message call from the proxy contract
               *
               * @dev Can be called by the user, or by a contract authorized by the registry as long as the user has not revoked access
               * @param dest Address to which the call will be sent
               * @param howToCall Which kind of call to make
               * @param calldata Calldata to send
               * @return Result of the call (success or failure)
               */
              function proxy(address dest, HowToCall howToCall, bytes calldata)
                  public
                  returns (bool result)
              {
                  require(msg.sender == user || (!revoked && registry.contracts(msg.sender)));
                  if (howToCall == HowToCall.Call) {
                      result = dest.call(calldata);
                  } else if (howToCall == HowToCall.DelegateCall) {
                      result = dest.delegatecall(calldata);
                  }
                  return result;
              }
          
              /**
               * Execute a message call and assert success
               * 
               * @dev Same functionality as `proxy`, just asserts the return value
               * @param dest Address to which the call will be sent
               * @param howToCall What kind of call to make
               * @param calldata Calldata to send
               */
              function proxyAssert(address dest, HowToCall howToCall, bytes calldata)
                  public
              {
                  require(proxy(dest, howToCall, calldata));
              }
          
          }
          
          contract Proxy {
          
            /**
            * @dev Tells the address of the implementation where every call will be delegated.
            * @return address of the implementation to which it will be delegated
            */
            function implementation() public view returns (address);
          
            /**
            * @dev Tells the type of proxy (EIP 897)
            * @return Type of proxy, 2 for upgradeable proxy
            */
            function proxyType() public pure returns (uint256 proxyTypeId);
          
            /**
            * @dev Fallback function allowing to perform a delegatecall to the given implementation.
            * This function will return whatever the implementation call returns
            */
            function () payable public {
              address _impl = implementation();
              require(_impl != address(0));
          
              assembly {
                let ptr := mload(0x40)
                calldatacopy(ptr, 0, calldatasize)
                let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0)
                let size := returndatasize
                returndatacopy(ptr, 0, size)
          
                switch result
                case 0 { revert(ptr, size) }
                default { return(ptr, size) }
              }
            }
          }
          
          contract OwnedUpgradeabilityProxy is Proxy, OwnedUpgradeabilityStorage {
            /**
            * @dev Event to show ownership has been transferred
            * @param previousOwner representing the address of the previous owner
            * @param newOwner representing the address of the new owner
            */
            event ProxyOwnershipTransferred(address previousOwner, address newOwner);
          
            /**
            * @dev This event will be emitted every time the implementation gets upgraded
            * @param implementation representing the address of the upgraded implementation
            */
            event Upgraded(address indexed implementation);
          
            /**
            * @dev Upgrades the implementation address
            * @param implementation representing the address of the new implementation to be set
            */
            function _upgradeTo(address implementation) internal {
              require(_implementation != implementation);
              _implementation = implementation;
              emit Upgraded(implementation);
            }
          
            /**
            * @dev Throws if called by any account other than the owner.
            */
            modifier onlyProxyOwner() {
              require(msg.sender == proxyOwner());
              _;
            }
          
            /**
             * @dev Tells the address of the proxy owner
             * @return the address of the proxy owner
             */
            function proxyOwner() public view returns (address) {
              return upgradeabilityOwner();
            }
          
            /**
             * @dev Allows the current owner to transfer control of the contract to a newOwner.
             * @param newOwner The address to transfer ownership to.
             */
            function transferProxyOwnership(address newOwner) public onlyProxyOwner {
              require(newOwner != address(0));
              emit ProxyOwnershipTransferred(proxyOwner(), newOwner);
              setUpgradeabilityOwner(newOwner);
            }
          
            /**
             * @dev Allows the upgradeability owner to upgrade the current implementation of the proxy.
             * @param implementation representing the address of the new implementation to be set.
             */
            function upgradeTo(address implementation) public onlyProxyOwner {
              _upgradeTo(implementation);
            }
          
            /**
             * @dev Allows the upgradeability owner to upgrade the current implementation of the proxy
             * and delegatecall the new implementation for initialization.
             * @param implementation representing the address of the new implementation to be set.
             * @param data represents the msg.data to bet sent in the low level call. This parameter may include the function
             * signature of the implementation to be called with the needed payload
             */
            function upgradeToAndCall(address implementation, bytes data) payable public onlyProxyOwner {
              upgradeTo(implementation);
              require(address(this).delegatecall(data));
            }
          }
          
          contract OwnableDelegateProxy is OwnedUpgradeabilityProxy {
          
              constructor(address owner, address initialImplementation, bytes calldata)
                  public
              {
                  setUpgradeabilityOwner(owner);
                  _upgradeTo(initialImplementation);
                  require(initialImplementation.delegatecall(calldata));
              }
          
          }