ETH Price: $2,707.69 (+2.75%)

Transaction Decoder

Block:
14013938 at Jan-16-2022 02:56:37 AM +UTC
Transaction Fee:
0.0336367449 ETH $91.08
Gas Used:
202,509 Gas / 166.1 Gwei

Emitted Events:

7 AdminUpgradeabilityProxy.0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62( 0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62, 0x000000000000000000000000211232b051d2ca58687968522518e76872768ec6, 0x000000000000000000000000b09f93d7aa0d0e8cbec2f36cbdcba2d7853f0fae, 0x0000000000000000000000001b5a2cea1ffed3e3cd286de4d62e96c7e64bb945, 7a9fe22691c811ea339d9b73150e6911a5343dca000000000000000004008001, 0000000000000000000000000000000000000000000000000000000000000001 )
8 WyvernExchange.OrdersMatched( buyHash=0000000000000000000000000000000000000000000000000000000000000000, sellHash=684B4D0CCDBD3849FBE217BEEC8B24C40118BA7A9A37F8B70F9EB827E0DC5FF5, maker=0xb09f93d7aa0d0e8cbec2f36cbdcba2d7853f0fae, taker=[Sender] 0x1b5a2cea1ffed3e3cd286de4d62e96c7e64bb945, price=19000000000000000, metadata=0000000000000000000000000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x1B5a2ceA...7E64bB945
0.07016418 Eth
Nonce: 0
0.0175274351 Eth
Nonce: 1
0.0526367449
0x5b325696...807C01073
(OpenSea: Wallet)
10,095.793416391828261677 Eth10,095.794841391828261677 Eth0.001425
0x7Be8076f...6C946D12b
0xa342f5D8...7f4478dB5
0xb09f93d7...7853F0FaE 1.448299558171722513 Eth1.465874558171722513 Eth0.017575
(Ethermine)
1,990.124078488535342811 Eth1,990.135959478251475116 Eth0.011880989716132305

Execution Trace

ETH 0.019 WyvernExchange.atomicMatch_( addrs=[0x7Be8076f4EA4A4AD08075C2508e481d6C946D12b, 0x1B5a2ceA1fFed3e3cd286de4d62e96C7E64bB945, 0xb09f93d7aa0D0e8CBEc2f36CBdcBA2d7853F0FaE, 0x0000000000000000000000000000000000000000, 0xa342f5D851E866E18ff98F351f2c6637f4478dB5, 0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000, 0x7Be8076f4EA4A4AD08075C2508e481d6C946D12b, 0xb09f93d7aa0D0e8CBEc2f36CBdcBA2d7853F0FaE, 0x0000000000000000000000000000000000000000, 0x5b3256965e7C3cF26E11FCAf296DfC8807C01073, 0xa342f5D851E866E18ff98F351f2c6637f4478dB5, 0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000], uints=[750, 0, 0, 0, 19000000000000000, 0, 1642301617, 0, 45304471964691186835266908895525160684787321397238559104274394598270727040535, 750, 0, 0, 0, 19000000000000000, 0, 1642289207, 1642894085, 21706997260939173753753815537452876708819597217113551933610176134382024486832], feeMethodsSidesKindsHowToCalls=[1, 0, 0, 0, 1, 1, 0, 0], calldataBuy=0xF242432A00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B5A2CEA1FFED3E3CD286DE4D62E96C7E64BB9457A9FE22691C811EA339D9B73150E6911A5343DCA000000000000000004008001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000A00000000000000000000000000000000000000000000000000000000000000000, calldataSell=0xF242432A000000000000000000000000B09F93D7AA0D0E8CBEC2F36CBDCBA2D7853F0FAE00000000000000000000000000000000000000000000000000000000000000007A9FE22691C811EA339D9B73150E6911A5343DCA000000000000000004008001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000A00000000000000000000000000000000000000000000000000000000000000000, replacementPatternBuy=0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, replacementPatternSell=0x000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, staticExtradataBuy=0x, staticExtradataSell=0x, vs=[27, 27], rssMetadata=[vx2wvDnA/kCcmoajbIU+SMaeVeBeZ5w1OtdEIFwHvqY=, J1lLXGHfRm41g9q6fQU8au9zjFFBpyqtB+D3WZmAti8=, vx2wvDnA/kCcmoajbIU+SMaeVeBeZ5w1OtdEIFwHvqY=, J1lLXGHfRm41g9q6fQU8au9zjFFBpyqtB+D3WZmAti8=, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=] )
  • Null: 0x000...001.684b4d0c( )
  • WyvernProxyRegistry.proxies( 0xb09f93d7aa0D0e8CBEc2f36CBdcBA2d7853F0FaE ) => ( 0x211232B051d2CA58687968522518e76872768EC6 )
  • WyvernProxyRegistry.CALL( )
  • OwnableDelegateProxy.CALL( )
  • ETH 0.001425 OpenSea: Wallet.CALL( )
  • ETH 0.017575 0xb09f93d7aa0d0e8cbec2f36cbdcba2d7853f0fae.CALL( )
  • OwnableDelegateProxy.1b0f7ba9( )
    • AuthenticatedProxy.proxy( dest=0xa342f5D851E866E18ff98F351f2c6637f4478dB5, howToCall=0, calldata=0xF242432A000000000000000000000000B09F93D7AA0D0E8CBEC2F36CBDCBA2D7853F0FAE0000000000000000000000001B5A2CEA1FFED3E3CD286DE4D62E96C7E64BB9457A9FE22691C811EA339D9B73150E6911A5343DCA000000000000000004008001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000A00000000000000000000000000000000000000000000000000000000000000000 ) => ( result=True )
      • WyvernProxyRegistry.contracts( 0x7Be8076f4EA4A4AD08075C2508e481d6C946D12b ) => ( True )
      • AdminUpgradeabilityProxy.f242432a( )
        • Asset.safeTransferFrom( from=0xb09f93d7aa0D0e8CBEc2f36CBdcBA2d7853F0FaE, to=0x1B5a2ceA1fFed3e3cd286de4d62e96C7E64bB945, id=55464657044963196816950587289035428064568320970692304673817341489687455367169, value=1, data=0x )
          File 1 of 6: 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 6: AdminUpgradeabilityProxy
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          import './UpgradeabilityProxy.sol';
          /**
           * @title AdminUpgradeabilityProxy
           * @dev This contract combines an upgradeability proxy with an authorization
           * mechanism for administrative tasks.
           * All external functions in this contract must be guarded by the
           * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
           * feature proposal that would enable this to be done automatically.
           */
          contract AdminUpgradeabilityProxy is UpgradeabilityProxy {
            /**
             * Contract constructor.
             * @param _logic address of the initial implementation.
             * @param _admin Address of the proxy administrator.
             * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
             * It should include the signature and the parameters of the function to be called, as described in
             * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
             * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
             */
            constructor(address _logic, address _admin, bytes memory _data) UpgradeabilityProxy(_logic, _data) public payable {
              assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1));
              _setAdmin(_admin);
            }
            /**
             * @dev Emitted when the administration has been transferred.
             * @param previousAdmin Address of the previous admin.
             * @param newAdmin Address of the new admin.
             */
            event AdminChanged(address previousAdmin, address newAdmin);
            /**
             * @dev Storage slot with the admin of the contract.
             * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
             * validated in the constructor.
             */
            bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
            /**
             * @dev Modifier to check whether the `msg.sender` is the admin.
             * If it is, it will run the function. Otherwise, it will delegate the call
             * to the implementation.
             */
            modifier ifAdmin() {
              if (msg.sender == _admin()) {
                _;
              } else {
                _fallback();
              }
            }
            /**
             * @return The address of the proxy admin.
             */
            function admin() external ifAdmin returns (address) {
              return _admin();
            }
            /**
             * @return The address of the implementation.
             */
            function implementation() external ifAdmin returns (address) {
              return _implementation();
            }
            /**
             * @dev Changes the admin of the proxy.
             * Only the current admin can call this function.
             * @param newAdmin Address to transfer proxy administration to.
             */
            function changeAdmin(address newAdmin) external ifAdmin {
              require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
              emit AdminChanged(_admin(), newAdmin);
              _setAdmin(newAdmin);
            }
            /**
             * @dev Upgrade the backing implementation of the proxy.
             * Only the admin can call this function.
             * @param newImplementation Address of the new implementation.
             */
            function upgradeTo(address newImplementation) external ifAdmin {
              _upgradeTo(newImplementation);
            }
            /**
             * @dev Upgrade the backing implementation of the proxy and call a function
             * on the new implementation.
             * This is useful to initialize the proxied contract.
             * @param newImplementation Address of the new implementation.
             * @param data Data to send as msg.data in the low level call.
             * It should include the signature and the parameters of the function to be called, as described in
             * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
             */
            function upgradeToAndCall(address newImplementation, bytes calldata data) payable external ifAdmin {
              _upgradeTo(newImplementation);
              (bool success,) = newImplementation.delegatecall(data);
              require(success);
            }
            /**
             * @return adm The admin slot.
             */
            function _admin() internal view returns (address adm) {
              bytes32 slot = ADMIN_SLOT;
              assembly {
                adm := sload(slot)
              }
            }
            /**
             * @dev Sets the address of the proxy admin.
             * @param newAdmin Address of the new proxy admin.
             */
            function _setAdmin(address newAdmin) internal {
              bytes32 slot = ADMIN_SLOT;
              assembly {
                sstore(slot, newAdmin)
              }
            }
            /**
             * @dev Only fall back when the sender is not the admin.
             */
            function _willFallback() internal override virtual {
              require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
              super._willFallback();
            }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          import './Proxy.sol';
          import '@openzeppelin/contracts/utils/Address.sol';
          /**
           * @title UpgradeabilityProxy
           * @dev This contract implements a proxy that allows to change the
           * implementation address to which it will delegate.
           * Such a change is called an implementation upgrade.
           */
          contract UpgradeabilityProxy is Proxy {
            /**
             * @dev Contract constructor.
             * @param _logic Address of the initial implementation.
             * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
             * It should include the signature and the parameters of the function to be called, as described in
             * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
             * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
             */
            constructor(address _logic, bytes memory _data) public payable {
              assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
              _setImplementation(_logic);
              if(_data.length > 0) {
                (bool success,) = _logic.delegatecall(_data);
                require(success);
              }
            }  
            /**
             * @dev Emitted when the implementation is upgraded.
             * @param implementation Address of the new implementation.
             */
            event Upgraded(address indexed implementation);
            /**
             * @dev Storage slot with the address of the current implementation.
             * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
             * validated in the constructor.
             */
            bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
            /**
             * @dev Returns the current implementation.
             * @return impl Address of the current implementation
             */
            function _implementation() internal override view returns (address impl) {
              bytes32 slot = IMPLEMENTATION_SLOT;
              assembly {
                impl := sload(slot)
              }
            }
            /**
             * @dev Upgrades the proxy to a new implementation.
             * @param newImplementation Address of the new implementation.
             */
            function _upgradeTo(address newImplementation) internal {
              _setImplementation(newImplementation);
              emit Upgraded(newImplementation);
            }
            /**
             * @dev Sets the implementation address of the proxy.
             * @param newImplementation Address of the new implementation.
             */
            function _setImplementation(address newImplementation) internal {
              require(Address.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
              bytes32 slot = IMPLEMENTATION_SLOT;
              assembly {
                sstore(slot, newImplementation)
              }
            }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          /**
           * @title Proxy
           * @dev Implements delegation of calls to other contracts, with proper
           * forwarding of return values and bubbling of failures.
           * It defines a fallback function that delegates all calls to the address
           * returned by the abstract _implementation() internal function.
           */
          abstract contract Proxy {
            /**
             * @dev Fallback function.
             * Implemented entirely in `_fallback`.
             */
            fallback () payable external {
              _fallback();
            }
            /**
             * @dev Receive function.
             * Implemented entirely in `_fallback`.
             */
            receive () payable external {
              _fallback();
            }
            /**
             * @return The Address of the implementation.
             */
            function _implementation() internal virtual view returns (address);
            /**
             * @dev Delegates execution to an implementation contract.
             * This is a low level function that doesn't return to its internal call site.
             * It will return to the external caller whatever the implementation returns.
             * @param implementation Address to delegate.
             */
            function _delegate(address implementation) internal {
              assembly {
                // Copy msg.data. We take full control of memory in this inline assembly
                // block because it will not return to Solidity code. We overwrite the
                // Solidity scratch pad at memory position 0.
                calldatacopy(0, 0, calldatasize())
                // Call the implementation.
                // out and outsize are 0 because we don't know the size yet.
                let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                // Copy the returned data.
                returndatacopy(0, 0, returndatasize())
                switch result
                // delegatecall returns 0 on error.
                case 0 { revert(0, returndatasize()) }
                default { return(0, returndatasize()) }
              }
            }
            /**
             * @dev Function that is run as the first thing in the fallback function.
             * Can be redefined in derived contracts to add functionality.
             * Redefinitions must call super._willFallback().
             */
            function _willFallback() internal virtual {
            }
            /**
             * @dev fallback implementation.
             * Extracted to enable manual triggering.
             */
            function _fallback() internal {
              _willFallback();
              _delegate(_implementation());
            }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity >=0.6.2 <0.8.0;
          /**
           * @dev Collection of functions related to the address type
           */
          library Address {
              /**
               * @dev Returns true if `account` is a contract.
               *
               * [IMPORTANT]
               * ====
               * It is unsafe to assume that an address for which this function returns
               * false is an externally-owned account (EOA) and not a contract.
               *
               * Among others, `isContract` will return false for the following
               * types of addresses:
               *
               *  - an externally-owned account
               *  - a contract in construction
               *  - an address where a contract will be created
               *  - an address where a contract lived, but was destroyed
               * ====
               */
              function isContract(address account) internal view returns (bool) {
                  // This method relies on extcodesize, which returns 0 for contracts in
                  // construction, since the code is only stored at the end of the
                  // constructor execution.
                  uint256 size;
                  // solhint-disable-next-line no-inline-assembly
                  assembly { size := extcodesize(account) }
                  return size > 0;
              }
              /**
               * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
               * `recipient`, forwarding all available gas and reverting on errors.
               *
               * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
               * of certain opcodes, possibly making contracts go over the 2300 gas limit
               * imposed by `transfer`, making them unable to receive funds via
               * `transfer`. {sendValue} removes this limitation.
               *
               * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
               *
               * IMPORTANT: because control is transferred to `recipient`, care must be
               * taken to not create reentrancy vulnerabilities. Consider using
               * {ReentrancyGuard} or the
               * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
               */
              function sendValue(address payable recipient, uint256 amount) internal {
                  require(address(this).balance >= amount, "Address: insufficient balance");
                  // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                  (bool success, ) = recipient.call{ value: amount }("");
                  require(success, "Address: unable to send value, recipient may have reverted");
              }
              /**
               * @dev Performs a Solidity function call using a low level `call`. A
               * plain`call` is an unsafe replacement for a function call: use this
               * function instead.
               *
               * If `target` reverts with a revert reason, it is bubbled up by this
               * function (like regular Solidity function calls).
               *
               * Returns the raw returned data. To convert to the expected return value,
               * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
               *
               * Requirements:
               *
               * - `target` must be a contract.
               * - calling `target` with `data` must not revert.
               *
               * _Available since v3.1._
               */
              function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                return functionCall(target, data, "Address: low-level call failed");
              }
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
               * `errorMessage` as a fallback revert reason when `target` reverts.
               *
               * _Available since v3.1._
               */
              function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                  return functionCallWithValue(target, data, 0, errorMessage);
              }
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
               * but also transferring `value` wei to `target`.
               *
               * Requirements:
               *
               * - the calling contract must have an ETH balance of at least `value`.
               * - the called Solidity function must be `payable`.
               *
               * _Available since v3.1._
               */
              function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                  return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
              }
              /**
               * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
               * with `errorMessage` as a fallback revert reason when `target` reverts.
               *
               * _Available since v3.1._
               */
              function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                  require(address(this).balance >= value, "Address: insufficient balance for call");
                  require(isContract(target), "Address: call to non-contract");
                  // solhint-disable-next-line avoid-low-level-calls
                  (bool success, bytes memory returndata) = target.call{ value: value }(data);
                  return _verifyCallResult(success, returndata, errorMessage);
              }
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
               * but performing a static call.
               *
               * _Available since v3.3._
               */
              function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                  return functionStaticCall(target, data, "Address: low-level static call failed");
              }
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
               * but performing a static call.
               *
               * _Available since v3.3._
               */
              function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
                  require(isContract(target), "Address: static call to non-contract");
                  // solhint-disable-next-line avoid-low-level-calls
                  (bool success, bytes memory returndata) = target.staticcall(data);
                  return _verifyCallResult(success, returndata, errorMessage);
              }
              function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
                  if (success) {
                      return returndata;
                  } else {
                      // Look for revert reason and bubble it up if present
                      if (returndata.length > 0) {
                          // The easiest way to bubble the revert reason is using memory via assembly
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              let returndata_size := mload(returndata)
                              revert(add(32, returndata), returndata_size)
                          }
                      } else {
                          revert(errorMessage);
                      }
                  }
              }
          }
          

          File 3 of 6: 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 4 of 6: OwnableDelegateProxy
          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 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 6: 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));
              }
          
          }

          File 6 of 6: Asset
          pragma solidity 0.5.9;
          import "./Asset/ERC1155ERC721.sol";
          contract Asset is ERC1155ERC721 {}
          pragma solidity 0.5.9;
          import "../contracts_common/Interfaces/ERC1155.sol";
          import "../contracts_common/Interfaces/ERC1155TokenReceiver.sol";
          import "../contracts_common/Libraries/AddressUtils.sol";
          import "../contracts_common/Libraries/ObjectLib32.sol";
          import "../contracts_common/Interfaces/ERC721.sol";
          import "../contracts_common/Interfaces/ERC721TokenReceiver.sol";
          import "../contracts_common/BaseWithStorage/SuperOperators.sol";
          contract ERC1155ERC721 is SuperOperators, ERC1155, ERC721 {
              using AddressUtils for address;
              using ObjectLib32 for ObjectLib32.Operations;
              using ObjectLib32 for uint256;
              bytes4 private constant ERC1155_IS_RECEIVER = 0x4e2312e0;
              bytes4 private constant ERC1155_RECEIVED = 0xf23a6e61;
              bytes4 private constant ERC1155_BATCH_RECEIVED = 0xbc197c81;
              bytes4 private constant ERC721_RECEIVED = 0x150b7a02;
              uint256 private constant CREATOR_OFFSET_MULTIPLIER = uint256(2)**(256 - 160);
              uint256 private constant IS_NFT_OFFSET_MULTIPLIER = uint256(2)**(256 - 160 - 1);
              uint256 private constant PACK_ID_OFFSET_MULTIPLIER = uint256(2)**(256 - 160 - 1 - 32 - 40);
              uint256 private constant PACK_NUM_FT_TYPES_OFFSET_MULTIPLIER = uint256(2)**(256 - 160 - 1 - 32 - 40 - 12);
              uint256 private constant NFT_INDEX_OFFSET = 63;
              uint256 private constant IS_NFT =            0x0000000000000000000000000000000000000000800000000000000000000000;
              uint256 private constant NOT_IS_NFT =        0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFFFFFFFFFFF;
              uint256 private constant NFT_INDEX =         0x00000000000000000000000000000000000000007FFFFFFF8000000000000000;
              uint256 private constant NOT_NFT_INDEX =     0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF800000007FFFFFFFFFFFFFFF;
              uint256 private constant URI_ID =            0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000007FFFFFFFFFFFF800;
              uint256 private constant PACK_ID =           0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000007FFFFFFFFF800000;
              uint256 private constant PACK_INDEX =        0x00000000000000000000000000000000000000000000000000000000000007FF;
              uint256 private constant PACK_NUM_FT_TYPES = 0x00000000000000000000000000000000000000000000000000000000007FF800;
              uint256 private constant MAX_SUPPLY = uint256(2)**32 - 1;
              uint256 private constant MAX_PACK_SIZE = uint256(2)**11;
              event CreatorshipTransfer(
                  address indexed original,
                  address indexed from,
                  address indexed to
              );
              mapping(address => uint256) private _numNFTPerAddress; // erc721
              mapping(uint256 => uint256) private _owners; // erc721
              mapping(address => mapping(uint256 => uint256)) private _packedTokenBalance; // erc1155
              mapping(address => mapping(address => bool)) private _operatorsForAll; // erc721 and erc1155
              mapping(uint256 => address) private _erc721operators; // erc721
              mapping(uint256 => bytes32) private _metadataHash; // erc721 and erc1155
              mapping(uint256 => bytes) private _rarityPacks; // rarity configuration per packs (2 bits per Asset)
              mapping(uint256 => uint32) private _nextCollectionIndex; // extraction
              mapping(address => address) private _creatorship; // creatorship transfer
              mapping(address => bool) private _bouncers; // the contracts allowed to mint
              mapping(address => bool) private _metaTransactionContracts; // native meta-transaction support
              address private _bouncerAdmin;
              bool internal _init;
              function init(
                  address metaTransactionContract,
                  address admin,
                  address bouncerAdmin
              ) public {
                  require(!_init, "ALREADY_INITIALISED");
                  _init = true;
                  _metaTransactionContracts[metaTransactionContract] = true;
                  _admin = admin;
                  _bouncerAdmin = bouncerAdmin;
                  emit MetaTransactionProcessor(metaTransactionContract, true);
              }
              event BouncerAdminChanged(address oldBouncerAdmin, address newBouncerAdmin);
              /// @notice Returns the current administrator in charge of minting rights.
              /// @return the current minting administrator in charge of minting rights.
              function getBouncerAdmin() external view returns(address) {
                  return _bouncerAdmin;
              }
              /// @notice Change the minting administrator to be `newBouncerAdmin`.
              /// @param newBouncerAdmin address of the new minting administrator.
              function changeBouncerAdmin(address newBouncerAdmin) external {
                  require(
                      msg.sender == _bouncerAdmin,
                      "only bouncerAdmin can change itself"
                  );
                  emit BouncerAdminChanged(_bouncerAdmin, newBouncerAdmin);
                  _bouncerAdmin = newBouncerAdmin;
              }
              event Bouncer(address bouncer, bool enabled);
              /// @notice Enable or disable the ability of `bouncer` to mint tokens (minting bouncer rights).
              /// @param bouncer address that will be given/removed minting bouncer rights.
              /// @param enabled set whether the address is enabled or disabled as a minting bouncer.
              function setBouncer(address bouncer, bool enabled) external {
                  require(
                      msg.sender == _bouncerAdmin,
                      "only bouncerAdmin can setup bouncers"
                  );
                  _bouncers[bouncer] = enabled;
                  emit Bouncer(bouncer, enabled);
              }
              /// @notice check whether address `who` is given minting bouncer rights.
              /// @param who The address to query.
              /// @return whether the address has minting rights.
              function isBouncer(address who) external view returns(bool) {
                  return _bouncers[who];
              }
              event MetaTransactionProcessor(address metaTransactionProcessor, bool enabled);
              /// @notice Enable or disable the ability of `metaTransactionProcessor` to perform meta-tx (metaTransactionProcessor rights).
              /// @param metaTransactionProcessor address that will be given/removed metaTransactionProcessor rights.
              /// @param enabled set whether the metaTransactionProcessor is enabled or disabled.
              function setMetaTransactionProcessor(address metaTransactionProcessor, bool enabled) external {
                  require(
                      msg.sender == _admin,
                      "only admin can setup metaTransactionProcessors"
                  );
                  _metaTransactionContracts[metaTransactionProcessor] = enabled;
                  emit MetaTransactionProcessor(metaTransactionProcessor, enabled);
              }
              /// @notice check whether address `who` is given meta-transaction execution rights.
              /// @param who The address to query.
              /// @return whether the address has meta-transaction execution rights.
              function isMetaTransactionProcessor(address who) external view returns(bool) {
                  return _metaTransactionContracts[who];
              }
              /// @notice Mint a token type for `creator` on slot `packId`.
              /// @param creator address of the creator of the token.
              /// @param packId unique packId for that token.
              /// @param hash hash of an IPFS cidv1 folder that contains the metadata of the token type in the file 0.json.
              /// @param supply number of tokens minted for that token type.
              /// @param rarity rarity power of the token.
              /// @param owner address that will receive the tokens.
              /// @param data extra data to accompany the minting call.
              /// @return the id of the newly minted token type.
              function mint(
                  address creator,
                  uint40 packId,
                  bytes32 hash,
                  uint256 supply,
                  uint8 rarity,
                  address owner,
                  bytes calldata data
              ) external returns (uint256 id) {
                  require(hash != 0, "hash is zero");
                  require(_bouncers[msg.sender], "only bouncer allowed to mint");
                  require(owner != address(0), "destination is zero address");
                  id = generateTokenId(creator, supply, packId, supply == 1 ? 0 : 1, 0);
                  _mint(
                      hash,
                      supply,
                      rarity,
                      msg.sender,
                      owner,
                      id,
                      data,
                      false
                  );
              }
              function generateTokenId(
                  address creator,
                  uint256 supply,
                  uint40 packId,
                  uint16 numFTs,
                  uint16 packIndex
              ) internal pure returns (uint256) {
                  require(supply > 0 && supply <= MAX_SUPPLY, "invalid supply");
                  return
                      uint256(creator) * CREATOR_OFFSET_MULTIPLIER + // CREATOR
                      (supply == 1 ? uint256(1) * IS_NFT_OFFSET_MULTIPLIER : 0) + // minted as NFT (1) or FT (0) // IS_NFT
                      uint256(packId) * PACK_ID_OFFSET_MULTIPLIER + // packId (unique pack) // PACk_ID
                      numFTs * PACK_NUM_FT_TYPES_OFFSET_MULTIPLIER + // number of fungible token in the pack // PACK_NUM_FT_TYPES
                      packIndex; // packIndex (position in the pack) // PACK_INDEX
              }
              function _mint(
                  bytes32 hash,
                  uint256 supply,
                  uint8 rarity,
                  address operator,
                  address owner,
                  uint256 id,
                  bytes memory data,
                  bool extraction
              ) internal {
                  uint256 uriId = id & URI_ID;
                  if (!extraction) {
                      require(uint256(_metadataHash[uriId]) == 0, "id already used");
                      _metadataHash[uriId] = hash;
                      require(rarity < 4, "rarity >= 4");
                      bytes memory pack = new bytes(1);
                      pack[0] = bytes1(rarity * 64);
                      _rarityPacks[uriId] = pack;
                  }
                  if (supply == 1) {
                      // ERC721
                      _numNFTPerAddress[owner]++;
                      _owners[id] = uint256(owner);
                      emit Transfer(address(0), owner, id);
                  } else {
                      (uint256 bin, uint256 index) = id.getTokenBinIndex();
                      _packedTokenBalance[owner][bin] = _packedTokenBalance[owner][bin]
                          .updateTokenBalance(
                          index,
                          supply,
                          ObjectLib32.Operations.REPLACE
                      );
                  }
                  emit TransferSingle(operator, address(0), owner, id, supply);
                  require(
                      _checkERC1155AndCallSafeTransfer(
                          operator,
                          address(0),
                          owner,
                          id,
                          supply,
                          data,
                          false,
                          false
                      ),
                      "transfer rejected"
                  );
              }
              /// @notice Mint multiple token types for `creator` on slot `packId`.
              /// @param creator address of the creator of the tokens.
              /// @param packId unique packId for the tokens.
              /// @param hash hash of an IPFS cidv1 folder that contains the metadata of each token type in the files: 0.json, 1.json, 2.json, etc...
              /// @param supplies number of tokens minted for each token type.
              /// @param rarityPack rarity power of each token types packed into 2 bits each.
              /// @param owner address that will receive the tokens.
              /// @param data extra data to accompany the minting call.
              /// @return the ids of each newly minted token types.
              function mintMultiple(
                  address creator,
                  uint40 packId,
                  bytes32 hash,
                  uint256[] calldata supplies,
                  bytes calldata rarityPack,
                  address owner,
                  bytes calldata data
              ) external returns (uint256[] memory ids) {
                  require(hash != 0, "hash is zero");
                  require(_bouncers[msg.sender], "only bouncer allowed to mint");
                  require(owner != address(0), "destination is zero address");
                  uint16 numNFTs;
                  (ids, numNFTs) = allocateIds(
                      creator,
                      supplies,
                      rarityPack,
                      packId,
                      hash
                  );
                  _mintBatches(supplies, owner, ids, numNFTs);
                  completeMultiMint(msg.sender, owner, ids, supplies, data);
              }
              function allocateIds(
                  address creator,
                  uint256[] memory supplies,
                  bytes memory rarityPack,
                  uint40 packId,
                  bytes32 hash
              ) internal returns (uint256[] memory ids, uint16 numNFTs) {
                  require(supplies.length > 0, "supplies.length == 0");
                  require(supplies.length <= MAX_PACK_SIZE, "too big batch");
                  (ids, numNFTs) = generateTokenIds(creator, supplies, packId);
                  uint256 uriId = ids[0] & URI_ID;
                  require(uint256(_metadataHash[uriId]) == 0, "id already used");
                  _metadataHash[uriId] = hash;
                  _rarityPacks[uriId] = rarityPack;
              }
              function generateTokenIds(
                  address creator,
                  uint256[] memory supplies,
                  uint40 packId
              ) internal pure returns (uint256[] memory, uint16) {
                  uint16 numTokenTypes = uint16(supplies.length);
                  uint256[] memory ids = new uint256[](numTokenTypes);
                  uint16 numNFTs = 0;
                  for (uint16 i = 0; i < numTokenTypes; i++) {
                      if (numNFTs == 0) {
                          if (supplies[i] == 1) {
                              numNFTs = uint16(numTokenTypes - i);
                          }
                      } else {
                          require(supplies[i] == 1, "NFTs need to be put at the end");
                      }
                  }
                  uint16 numFTs = numTokenTypes - numNFTs;
                  for (uint16 i = 0; i < numTokenTypes; i++) {
                      ids[i] = generateTokenId(creator, supplies[i], packId, numFTs, i);
                  }
                  return (ids, numNFTs);
              }
              function completeMultiMint(
                  address operator,
                  address owner,
                  uint256[] memory ids,
                  uint256[] memory supplies,
                  bytes memory data
              ) internal {
                  emit TransferBatch(operator, address(0), owner, ids, supplies);
                  require(
                      _checkERC1155AndCallSafeBatchTransfer(
                          operator,
                          address(0),
                          owner,
                          ids,
                          supplies,
                          data
                      ),
                      "transfer rejected"
                  );
              }
              function _mintBatches(
                  uint256[] memory supplies,
                  address owner,
                  uint256[] memory ids,
                  uint16 numNFTs
              ) internal {
                  uint16 offset = 0;
                  while (offset < supplies.length - numNFTs) {
                      _mintBatch(offset, supplies, owner, ids);
                      offset += 8;
                  }
                  // deal with NFT last. they do not care of balance packing
                  if (numNFTs > 0) {
                      _mintNFTs(
                          uint16(supplies.length - numNFTs),
                          numNFTs,
                          owner,
                          ids
                      );
                  }
              }
              function _mintNFTs(
                  uint16 offset,
                  uint32 numNFTs,
                  address owner,
                  uint256[] memory ids
              ) internal {
                  for (uint16 i = 0; i < numNFTs; i++) {
                      uint256 id = ids[i + offset];
                      _owners[id] = uint256(owner);
                      emit Transfer(address(0), owner, id);
                  }
                  _numNFTPerAddress[owner] += numNFTs;
              }
              function _mintBatch(
                  uint16 offset,
                  uint256[] memory supplies,
                  address owner,
                  uint256[] memory ids
              ) internal {
                  uint256 firstId = ids[offset];
                  (uint256 bin, uint256 index) = firstId.getTokenBinIndex();
                  uint256 balances = _packedTokenBalance[owner][bin];
                  for (uint256 i = 0; i < 8 && offset + i < supplies.length; i++) {
                      uint256 j = offset + i;
                      if (supplies[j] > 1) {
                          balances = balances.updateTokenBalance(
                              index + i,
                              supplies[j],
                              ObjectLib32.Operations.REPLACE
                          );
                      } else {
                          break;
                      }
                  }
                  _packedTokenBalance[owner][bin] = balances;
              }
              function _transferFrom(
                  address from,
                  address to,
                  uint256 id,
                  uint256 value
              ) internal returns (bool metaTx) {
                  require(to != address(0), "destination is zero address");
                  require(from != address(0), "from is zero address");
                  metaTx = _metaTransactionContracts[msg.sender];
                  bool authorized = from == msg.sender ||
                      metaTx ||
                      _superOperators[msg.sender] ||
                      _operatorsForAll[from][msg.sender];
                  if (id & IS_NFT > 0) {
                      require(
                          authorized || _erc721operators[id] == msg.sender,
                          "Operator not approved"
                      );
                      if(value > 0) {
                          require(value == 1, "cannot transfer nft if amount not 1");
                          _numNFTPerAddress[from]--;
                          _numNFTPerAddress[to]++;
                          _owners[id] = uint256(to);
                          if (_erc721operators[id] != address(0)) { // TODO operatorEnabled flag optimization (like in ERC721BaseToken)
                              _erc721operators[id] = address(0);
                          }
                          emit Transfer(from, to, id);
                      }
                  } else {
                      require(authorized, "Operator not approved");
                      if(value > 0) {
                          // if different owners it will fails
                          (uint256 bin, uint256 index) = id.getTokenBinIndex();
                          _packedTokenBalance[from][bin] = _packedTokenBalance[from][bin]
                              .updateTokenBalance(index, value, ObjectLib32.Operations.SUB);
                          _packedTokenBalance[to][bin] = _packedTokenBalance[to][bin]
                              .updateTokenBalance(index, value, ObjectLib32.Operations.ADD);
                      }
                  }
                  emit TransferSingle(
                      metaTx ? from : msg.sender,
                      from,
                      to,
                      id,
                      value
                  );
              }
              /// @notice Transfers `value` tokens of type `id` from  `from` to `to`  (with safety call).
              /// @param from address from which tokens are transfered.
              /// @param to address to which the token will be transfered.
              /// @param id the token type transfered.
              /// @param value amount of token transfered.
              /// @param data aditional data accompanying the transfer.
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 id,
                  uint256 value,
                  bytes calldata data
              ) external {
                  if (id & IS_NFT > 0) {
                      require(_ownerOf(id) == from, "not owner");
                  }
                  bool metaTx = _transferFrom(from, to, id, value);
                  require(
                      _checkERC1155AndCallSafeTransfer(
                          metaTx ? from : msg.sender,
                          from,
                          to,
                          id,
                          value,
                          data,
                          false,
                          false
                      ),
                      "erc1155 transfer rejected"
                  );
              }
              /// @notice Transfers `values` tokens of type `ids` from  `from` to `to` (with safety call).
              /// @dev call data should be optimized to order ids so packedBalance can be used efficiently.
              /// @param from address from which tokens are transfered.
              /// @param to address to which the token will be transfered.
              /// @param ids ids of each token type transfered.
              /// @param values amount of each token type transfered.
              /// @param data aditional data accompanying the transfer.
              function safeBatchTransferFrom(
                  address from,
                  address to,
                  uint256[] calldata ids,
                  uint256[] calldata values,
                  bytes calldata data
              ) external {
                  require(
                      ids.length == values.length,
                      "Inconsistent array length between args"
                  );
                  require(to != address(0), "destination is zero address");
                  require(from != address(0), "from is zero address");
                  bool metaTx = _metaTransactionContracts[msg.sender];
                  bool authorized = from == msg.sender ||
                      metaTx ||
                      _superOperators[msg.sender] ||
                      _operatorsForAll[from][msg.sender]; // solium-disable-line max-len
                  _batchTransferFrom(from, to, ids, values, authorized);
                  emit TransferBatch(
                      metaTx ? from : msg.sender,
                      from,
                      to,
                      ids,
                      values
                  );
                  require(
                      _checkERC1155AndCallSafeBatchTransfer(
                          metaTx ? from : msg.sender,
                          from,
                          to,
                          ids,
                          values,
                          data
                      ),
                      "erc1155 transfer rejected"
                  );
              }
              function _batchTransferFrom(
                  address from,
                  address to,
                  uint256[] memory ids,
                  uint256[] memory values,
                  bool authorized
              ) internal {
                  uint256 numItems = ids.length;
                  uint256 bin;
                  uint256 index;
                  uint256 balFrom;
                  uint256 balTo;
                  uint256 lastBin;
                  uint256 numNFTs = 0;
                  for (uint256 i = 0; i < numItems; i++) {
                      if (ids[i] & IS_NFT > 0) {
                          require(
                              authorized || _erc721operators[ids[i]] == msg.sender,
                              "Operator not approved"
                          );
                          if(values[i] > 0) {
                              require(values[i] == 1, "cannot transfer nft if amount not 1");
                              require(_ownerOf(ids[i]) == from, "not owner");
                              numNFTs++;
                              _owners[ids[i]] = uint256(to);
                              if (_erc721operators[ids[i]] != address(0)) { // TODO operatorEnabled flag optimization (like in ERC721BaseToken)
                                  _erc721operators[ids[i]] = address(0);
                              }
                              emit Transfer(from, to, ids[i]);
                          }
                      } else {
                          require(authorized, "Operator not approved");
                          if (from == to) {
                              _checkEnoughBalance(from, ids[i], values[i]);
                          } else if(values[i] > 0) {
                              (bin, index) = ids[i].getTokenBinIndex();
                              if (lastBin == 0) {
                                  lastBin = bin;
                                  balFrom = ObjectLib32.updateTokenBalance(
                                      _packedTokenBalance[from][bin],
                                      index,
                                      values[i],
                                      ObjectLib32.Operations.SUB
                                  );
                                  balTo = ObjectLib32.updateTokenBalance(
                                      _packedTokenBalance[to][bin],
                                      index,
                                      values[i],
                                      ObjectLib32.Operations.ADD
                                  );
                              } else {
                                  if (bin != lastBin) {
                                      _packedTokenBalance[from][lastBin] = balFrom;
                                      _packedTokenBalance[to][lastBin] = balTo;
                                      balFrom = _packedTokenBalance[from][bin];
                                      balTo = _packedTokenBalance[to][bin];
                                      lastBin = bin;
                                  }
                                  balFrom = balFrom.updateTokenBalance(
                                      index,
                                      values[i],
                                      ObjectLib32.Operations.SUB
                                  );
                                  balTo = balTo.updateTokenBalance(
                                      index,
                                      values[i],
                                      ObjectLib32.Operations.ADD
                                  );
                              }
                          }
                      }
                  }
                  if (numNFTs > 0 && from != to) {
                      _numNFTPerAddress[from] -= numNFTs;
                      _numNFTPerAddress[to] += numNFTs;
                  }
                  if (bin != 0 && from != to) {
                      _packedTokenBalance[from][bin] = balFrom;
                      _packedTokenBalance[to][bin] = balTo;
                  }
              }
              function _checkEnoughBalance(address from, uint256 id, uint256 value) internal {
                  (uint256 bin, uint256 index) = id.getTokenBinIndex();
                  require(_packedTokenBalance[from][bin].getValueInBin(index) >= value, "can't substract more than there is");
              }
              /// @notice Get the balance of `owner` for the token type `id`.
              /// @param owner The address of the token holder.
              /// @param id the token type of which to get the balance of.
              /// @return the balance of `owner` for the token type `id`.
              function balanceOf(address owner, uint256 id)
                  public
                  view
                  returns (uint256)
              {
                  // do not check for existence, balance is zero if never minted
                  // require(wasEverMinted(id), "token was never minted");
                  if (id & IS_NFT > 0) {
                      if (_ownerOf(id) == owner) {
                          return 1;
                      } else {
                          return 0;
                      }
                  }
                  (uint256 bin, uint256 index) = id.getTokenBinIndex();
                  return _packedTokenBalance[owner][bin].getValueInBin(index);
              }
              /// @notice Get the balance of `owners` for each token type `ids`.
              /// @param owners the addresses of the token holders queried.
              /// @param ids ids of each token type to query.
              /// @return the balance of each `owners` for each token type `ids`.
              function balanceOfBatch(
                  address[] calldata owners,
                  uint256[] calldata ids
              ) external view returns (uint256[] memory) {
                  require(
                      owners.length == ids.length,
                      "Inconsistent array length between args"
                  );
                  uint256[] memory balances = new uint256[](ids.length);
                  for (uint256 i = 0; i < ids.length; i++) {
                      balances[i] = balanceOf(owners[i], ids[i]);
                  }
                  return balances;
              }
              /// @notice Get the creator of the token type `id`.
              /// @param id the id of the token to get the creator of.
              /// @return the creator of the token type `id`.
              function creatorOf(uint256 id) external view returns (address) {
                  require(wasEverMinted(id), "token was never minted");
                  address originalCreator = address(id / CREATOR_OFFSET_MULTIPLIER);
                  address newCreator = _creatorship[originalCreator];
                  if (newCreator != address(0)) {
                      return newCreator;
                  }
                  return originalCreator;
              }
              /// @notice Transfers creatorship of `original` from `sender` to `to`.
              /// @param sender address of current registered creator.
              /// @param original address of the original creator whose creation are saved in the ids themselves.
              /// @param to address which will be given creatorship for all tokens originally minted by `original`.
              function transferCreatorship(
                  address sender,
                  address original,
                  address to
              ) external {
                  require(
                      msg.sender == sender ||
                      _metaTransactionContracts[msg.sender] ||
                      _superOperators[msg.sender],
                      "require meta approval"
                  );
                  require(sender != address(0), "sender is zero address");
                  require(to != address(0), "destination is zero address");
                  address current = _creatorship[original];
                  if (current == address(0)) {
                      current = original;
                  }
                  require(current != to, "current == to");
                  require(current == sender, "current != sender");
                  if (to == original) {
                      _creatorship[original] = address(0);
                  } else {
                      _creatorship[original] = to;
                  }
                  emit CreatorshipTransfer(original, current, to);
              }
              /// @notice Enable or disable approval for `operator` to manage all `sender`'s tokens.
              /// @dev used for Meta Transaction (from metaTransactionContract).
              /// @param sender address which grant approval.
              /// @param operator address which will be granted rights to transfer all token owned by `sender`.
              /// @param approved whether to approve or revoke.
              function setApprovalForAllFor(
                  address sender,
                  address operator,
                  bool approved
              ) external {
                  require(
                      msg.sender == sender ||
                      _metaTransactionContracts[msg.sender] ||
                      _superOperators[msg.sender],
                      "require meta approval"
                  );
                  _setApprovalForAll(sender, operator, approved);
              }
              /// @notice Enable or disable approval for `operator` to manage all of the caller's tokens.
              /// @param operator address which will be granted rights to transfer all tokens of the caller.
              /// @param approved whether to approve or revoke
              function setApprovalForAll(address operator, bool approved) external {
                  _setApprovalForAll(msg.sender, operator, approved);
              }
              function _setApprovalForAll(
                  address sender,
                  address operator,
                  bool approved
              ) internal {
                  require(sender != address(0), "sender is zero address");
                  require(sender != operator, "sender = operator");
                  require(operator != address(0), "operator is zero address");
                  require(
                      !_superOperators[operator],
                      "super operator can't have their approvalForAll changed"
                  );
                  _operatorsForAll[sender][operator] = approved;
                  emit ApprovalForAll(sender, operator, approved);
              }
              /// @notice Queries the approval status of `operator` for owner `owner`.
              /// @param owner the owner of the tokens.
              /// @param operator address of authorized operator.
              /// @return true if the operator is approved, false if not.
              function isApprovedForAll(address owner, address operator)
                  external
                  view
                  returns (bool isOperator)
              {
                  require(owner != address(0), "owner is zero address");
                  require(operator != address(0), "operator is zero address");
                  return _operatorsForAll[owner][operator] || _superOperators[operator];
              }
              /// @notice Count all NFTs assigned to `owner`.
              /// @param owner address for whom to query the balance.
              /// @return the number of NFTs owned by `owner`, possibly zero.
              function balanceOf(address owner)
                  external
                  view
                  returns (uint256 balance)
              {
                  require(owner != address(0), "owner is zero address");
                  return _numNFTPerAddress[owner];
              }
              /// @notice Find the owner of an NFT.
              /// @param id the identifier for an NFT.
              /// @return the address of the owner of the NFT.
              function ownerOf(uint256 id) external view returns (address owner) {
                  owner = _ownerOf(id);
                  require(owner != address(0), "NFT does not exist");
              }
              function _ownerOf(uint256 id) internal view returns (address) {
                  return address(_owners[id]);
              }
              /// @notice Change or reaffirm the approved address for an NFT for `sender`.
              /// @dev used for Meta Transaction (from metaTransactionContract).
              /// @param sender the sender granting control.
              /// @param operator the address to approve as NFT controller.
              /// @param id the NFT to approve.
              function approveFor(address sender, address operator, uint256 id)
                  external
              {
                  address owner = _ownerOf(id);
                  require(sender != address(0), "sender is zero address");
                  require(
                      msg.sender == sender ||
                      _metaTransactionContracts[msg.sender] ||
                      _superOperators[msg.sender] ||
                      _operatorsForAll[sender][msg.sender],
                      "require operators"
                  ); // solium-disable-line max-len
                  require(owner == sender, "not owner");
                  _erc721operators[id] = operator;
                  emit Approval(owner, operator, id);
              }
              /// @notice Change or reaffirm the approved address for an NFT.
              /// @param operator the address to approve as NFT controller.
              /// @param id the id of the NFT to approve.
              function approve(address operator, uint256 id) external {
                  address owner = _ownerOf(id);
                  require(owner != address(0), "NFT does not exist");
                  require(
                      owner == msg.sender ||
                      _superOperators[msg.sender] ||
                      _operatorsForAll[owner][msg.sender],
                      "not authorized"
                  );
                  _erc721operators[id] = operator;
                  emit Approval(owner, operator, id);
              }
              /// @notice Get the approved address for a single NFT.
              /// @param id the NFT to find the approved address for.
              /// @return the approved address for this NFT, or the zero address if there is none.
              function getApproved(uint256 id)
                  external
                  view
                  returns (address operator)
              {
                  require(_ownerOf(id) != address(0), "NFT does not exist");
                  return _erc721operators[id];
              }
              /// @notice Transfers ownership of an NFT.
              /// @param from the current owner of the NFT.
              /// @param to the new owner.
              /// @param id the NFT to transfer.
              function transferFrom(address from, address to, uint256 id) external {
                  require(_ownerOf(id) == from, "not owner");
                  bool metaTx = _transferFrom(from, to, id, 1);
                  require(
                      _checkERC1155AndCallSafeTransfer(
                          metaTx ? from : msg.sender,
                          from,
                          to,
                          id,
                          1,
                          "",
                          true,
                          false
                      ),
                      "erc1155 transfer rejected"
                  );
              }
              /// @notice Transfers the ownership of an NFT from one address to another address.
              /// @param from the current owner of the NFT.
              /// @param to the new owner.
              /// @param id the NFT to transfer.
              function safeTransferFrom(address from, address to, uint256 id)
                  external
              {
                  safeTransferFrom(from, to, id, "");
              }
              /// @notice Transfers the ownership of an NFT from one address to another address.
              /// @param from the current owner of the NFT.
              /// @param to the new owner.
              /// @param id the NFT to transfer.
              /// @param data additional data with no specified format, sent in call to `to`.
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 id,
                  bytes memory data
              ) public {
                  require(_ownerOf(id) == from, "not owner");
                  bool metaTx = _transferFrom(from, to, id, 1);
                  require(
                      _checkERC1155AndCallSafeTransfer(
                          metaTx ? from : msg.sender,
                          from,
                          to,
                          id,
                          1,
                          data,
                          true,
                          true
                      ),
                      "erc721/erc1155 transfer rejected"
                  );
              }
              /// @notice A descriptive name for the collection of tokens in this contract.
              /// @return the name of the tokens.
              function name() external pure returns (string memory _name) {
                  return "Sandbox's ASSETs";
              }
              /// @notice An abbreviated name for the collection of tokens in this contract.
              /// @return the symbol of the tokens.
              function symbol() external pure returns (string memory _symbol) {
                  return "ASSET";
              }
              /// @notice Gives the rarity power of a particular token type.
              /// @param id the token type to get the rarity of.
              /// @return the rarity power(between 0 and 3).
              function rarity(uint256 id) public view returns (uint256) {
                  require(wasEverMinted(id), "token was never minted");
                  bytes storage rarityPack = _rarityPacks[id & URI_ID];
                  uint256 packIndex = id & PACK_INDEX;
                  if (packIndex / 4 >= rarityPack.length) {
                      return 0;
                  } else {
                      uint8 pack = uint8(rarityPack[packIndex / 4]);
                      uint8 i = (3 - uint8(packIndex % 4)) * 2;
                      return (pack / (uint8(2)**i)) % 4;
                  }
              }
              /// @notice Gives the collection a specific token belongs to.
              /// @param id the token to get the collection of.
              /// @return the collection the NFT is part of.
              function collectionOf(uint256 id) public view returns (uint256) {
                  require(_ownerOf(id) != address(0), "NFT does not exist");
                  uint256 collectionId = id & NOT_NFT_INDEX & NOT_IS_NFT;
                  require(wasEverMinted(collectionId), "no collection ever minted for that token");
                  return collectionId;
              }
              /// @notice Return wether the id is a collection
              /// @param id collectionId to check.
              /// @return whether the id is a collection.
              function isCollection(uint256 id) public view returns (bool) {
                  uint256 collectionId = id & NOT_NFT_INDEX & NOT_IS_NFT;
                  return wasEverMinted(collectionId);
              }
              /// @notice Gives the index at which an NFT was minted in a collection : first of a collection get the zero index.
              /// @param id the token to get the index of.
              /// @return the index/order at which the token `id` was minted in a collection.
              function collectionIndexOf(uint256 id) public view returns (uint256) {
                  collectionOf(id); // this check if id and collection indeed was ever minted
                  return uint32((id & NFT_INDEX) >> NFT_INDEX_OFFSET);
              }
              function toFullURI(bytes32 hash, uint256 id)
                  internal
                  pure
                  returns (string memory)
              {
                  return
                      string(
                          abi.encodePacked(
                              "ipfs://bafybei",
                              hash2base32(hash),
                              "/",
                              uint2str(id & PACK_INDEX),
                              ".json"
                          )
                      );
              }
              function wasEverMinted(uint256 id) public view returns(bool) {
                  if ((id & IS_NFT) > 0) {
                      return _owners[id] != 0;
                  } else {
                      return
                          ((id & PACK_INDEX) < ((id & PACK_NUM_FT_TYPES) / PACK_NUM_FT_TYPES_OFFSET_MULTIPLIER)) &&
                          _metadataHash[id & URI_ID] != 0;
                  }
              }
              /// @notice check whether a packId/numFT tupple has been used
              /// @param creator for which creator
              /// @param packId the packId to check
              /// @param numFTs number of Fungible Token in that pack (can reuse packId if different)
              /// @return whether the pack has already been used
              function isPackIdUsed(address creator, uint40 packId, uint16 numFTs) external returns(bool) {
                  uint256 uriId = uint256(creator) * CREATOR_OFFSET_MULTIPLIER + // CREATOR
                      uint256(packId) * PACK_ID_OFFSET_MULTIPLIER + // packId (unique pack) // PACk_ID
                      numFTs * PACK_NUM_FT_TYPES_OFFSET_MULTIPLIER; // number of fungible token in the pack // PACK_NUM_FT_TYPES
                  return _metadataHash[uriId] != 0;
              }
              /// @notice A distinct Uniform Resource Identifier (URI) for a given token.
              /// @param id token to get the uri of.
              /// @return URI string
              function uri(uint256 id) public view returns (string memory) {
                  require(wasEverMinted(id), "token was never minted"); // prevent returning invalid uri
                  return toFullURI(_metadataHash[id & URI_ID], id);
              }
              /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
              /// @param id token to get the uri of.
              /// @return URI string
              function tokenURI(uint256 id) public view returns (string memory) {
                  require(_ownerOf(id) != address(0), "NFT does not exist");
                  return toFullURI(_metadataHash[id & URI_ID], id);
              }
              bytes32 private constant base32Alphabet = 0x6162636465666768696A6B6C6D6E6F707172737475767778797A323334353637;
              // solium-disable-next-line security/no-assign-params
              function hash2base32(bytes32 hash)
                  private
                  pure
                  returns (string memory _uintAsString)
              {
                  uint256 _i = uint256(hash);
                  uint256 k = 52;
                  bytes memory bstr = new bytes(k);
                  bstr[--k] = base32Alphabet[uint8((_i % 8) << 2)]; // uint8 s = uint8((256 - skip) % 5);  // (_i % (2**s)) << (5-s)
                  _i /= 8;
                  while (k > 0) {
                      bstr[--k] = base32Alphabet[_i % 32];
                      _i /= 32;
                  }
                  return string(bstr);
              }
              // solium-disable-next-line security/no-assign-params
              function uint2str(uint256 _i)
                  private
                  pure
                  returns (string memory _uintAsString)
              {
                  if (_i == 0) {
                      return "0";
                  }
                  uint256 j = _i;
                  uint256 len;
                  while (j != 0) {
                      len++;
                      j /= 10;
                  }
                  bytes memory bstr = new bytes(len);
                  uint256 k = len - 1;
                  while (_i != 0) {
                      bstr[k--] = bytes1(uint8(48 + (_i % 10)));
                      _i /= 10;
                  }
                  return string(bstr);
              }
              /// @notice Query if a contract implements interface `id`.
              /// @param id the interface identifier, as specified in ERC-165.
              /// @return `true` if the contract implements `id`.
              function supportsInterface(bytes4 id) external view returns (bool) {
                  return
                      id == 0x01ffc9a7 || //ERC165
                      id == 0xd9b67a26 || // ERC1155
                      id == 0x80ac58cd || // ERC721
                      id == 0x5b5e139f || // ERC721 metadata
                      id == 0x0e89341c; // ERC1155 metadata
              }
              bytes4 constant ERC165ID = 0x01ffc9a7;
              function checkIsERC1155Receiver(address _contract)
                  internal
                  view
                  returns (bool)
              {
                  bool success;
                  bool result;
                  bytes memory call_data = abi.encodeWithSelector(
                      ERC165ID,
                      ERC1155_IS_RECEIVER
                  );
                  // solium-disable-next-line security/no-inline-assembly
                  assembly {
                      let call_ptr := add(0x20, call_data)
                      let call_size := mload(call_data)
                      let output := mload(0x40) // Find empty storage location using "free memory pointer"
                      mstore(output, 0x0)
                      success := staticcall(
                          10000,
                          _contract,
                          call_ptr,
                          call_size,
                          output,
                          0x20
                      ) // 32 bytes
                      result := mload(output)
                  }
                  // (10000 / 63) "not enough for supportsInterface(...)" // consume all gas, so caller can potentially know that there was not enough gas
                  assert(gasleft() > 158);
                  return success && result;
              }
              function _checkERC1155AndCallSafeTransfer(
                  address operator,
                  address from,
                  address to,
                  uint256 id,
                  uint256 value,
                  bytes memory data,
                  bool erc721,
                  bool erc721Safe
              ) internal returns (bool) {
                  if (!to.isContract()) {
                      return true;
                  }
                  if (erc721) {
                      if (!checkIsERC1155Receiver(to)) {
                          if (erc721Safe) {
                              return
                                  _checkERC721AndCallSafeTransfer(
                                      operator,
                                      from,
                                      to,
                                      id,
                                      data
                                  );
                          } else {
                              return true;
                          }
                      }
                  }
                  return
                      ERC1155TokenReceiver(to).onERC1155Received(
                              operator,
                              from,
                              id,
                              value,
                              data
                      ) == ERC1155_RECEIVED;
              }
              function _checkERC1155AndCallSafeBatchTransfer(
                  address operator,
                  address from,
                  address to,
                  uint256[] memory ids,
                  uint256[] memory values,
                  bytes memory data
              ) internal returns (bool) {
                  if (!to.isContract()) {
                      return true;
                  }
                  bytes4 retval = ERC1155TokenReceiver(to).onERC1155BatchReceived(
                      operator,
                      from,
                      ids,
                      values,
                      data
                  );
                  return (retval == ERC1155_BATCH_RECEIVED);
              }
              function _checkERC721AndCallSafeTransfer(
                  address operator,
                  address from,
                  address to,
                  uint256 id,
                  bytes memory data
              ) internal returns (bool) {
                  // following not required as this function is always called as part of ERC1155 checks that include such check already
                  // if (!to.isContract()) {
                  //     return true;
                  // }
                  return (ERC721TokenReceiver(to).onERC721Received(
                          operator,
                          from,
                          id,
                          data
                      ) ==
                      ERC721_RECEIVED);
              }
              event Extraction(uint256 indexed fromId, uint256 toId);
              event AssetUpdate(uint256 indexed fromId, uint256 toId);
              function _burnERC1155(
                  address operator,
                  address from,
                  uint256 id,
                  uint32 amount
              ) internal {
                  (uint256 bin, uint256 index) = (id).getTokenBinIndex();
                  _packedTokenBalance[from][bin] = _packedTokenBalance[from][bin]
                      .updateTokenBalance(index, amount, ObjectLib32.Operations.SUB);
                  emit TransferSingle(operator, from, address(0), id, amount);
              }
              function _burnERC721(address operator, address from, uint256 id)
                  internal
              {
                  require(from == _ownerOf(id), "not owner");
                  _owners[id] = 2**160; // equivalent to zero address when casted but ensure we track minted status
                  _numNFTPerAddress[from]--;
                  emit Transfer(from, address(0), id);
                  emit TransferSingle(operator, from, address(0), id, 1);
              }
              /// @notice Burns `amount` tokens of type `id`.
              /// @param id token type which will be burnt.
              /// @param amount amount of token to burn.
              function burn(uint256 id, uint256 amount) external {
                  _burn(msg.sender, id, amount);
              }
              /// @notice Burns `amount` tokens of type `id` from `from`.
              /// @param from address whose token is to be burnt.
              /// @param id token type which will be burnt.
              /// @param amount amount of token to burn.
              function burnFrom(address from, uint256 id, uint256 amount) external {
                  require(from != address(0), "from is zero address");
                  require(
                      msg.sender == from ||
                          _metaTransactionContracts[msg.sender] ||
                          _superOperators[msg.sender] ||
                          _operatorsForAll[from][msg.sender],
                      "require meta approval"
                  );
                  _burn(from, id, amount);
              }
              function _burn(address from, uint256 id, uint256 amount) internal {
                  if ((id & IS_NFT) > 0) {
                      require(amount == 1, "can only burn one NFT");
                      _burnERC721(
                          _metaTransactionContracts[msg.sender] ? from : msg.sender,
                          from,
                          id
                      );
                  } else {
                      require(amount > 0 && amount <= MAX_SUPPLY, "invalid amount");
                      _burnERC1155(
                          _metaTransactionContracts[msg.sender] ? from : msg.sender,
                          from,
                          id,
                          uint32(amount)
                      );
                  }
              }
              /// @notice Upgrades an NFT with new metadata and rarity.
              /// @param from address which own the NFT to be upgraded.
              /// @param id the NFT that will be burnt to be upgraded.
              /// @param packId unqiue packId for the token.
              /// @param hash hash of an IPFS cidv1 folder that contains the metadata of the new token type in the file 0.json.
              /// @param newRarity rarity power of the new NFT.
              /// @param to address which will receive the NFT.
              /// @param data bytes to be transmitted as part of the minted token.
              /// @return the id of the newly minted NFT.
              function updateERC721(
                  address from,
                  uint256 id,
                  uint40 packId,
                  bytes32 hash,
                  uint8 newRarity,
                  address to,
                  bytes calldata data
              ) external returns(uint256) {
                  require(hash != 0, "hash is zero");
                  require(
                      _bouncers[msg.sender],
                      "only bouncer allowed to mint via update"
                  );
                  require(to != address(0), "destination is zero address");
                  require(from != address(0), "from is zero address");
                  _burnERC721(msg.sender, from, id);
                  uint256 newId = generateTokenId(from, 1, packId, 0, 0);
                  _mint(hash, 1, newRarity, msg.sender, to, newId, data, false);
                  emit AssetUpdate(id, newId);
                  return newId;
              }
              /// @notice Extracts an EIP-721 NFT from an EIP-1155 token.
              /// @param id the token type to extract from.
              /// @param to address which will receive the token.
              /// @return the id of the newly minted NFT.
              function extractERC721(uint256 id, address to)
                  external
                  returns (uint256 newId)
              {
                  return _extractERC721From(msg.sender, msg.sender, id, to);
              }
              /// @notice Extracts an EIP-721 NFT from an EIP-1155 token.
              /// @param sender address which own the token to be extracted.
              /// @param id the token type to extract from.
              /// @param to address which will receive the token.
              /// @return the id of the newly minted NFT.
              function extractERC721From(address sender, uint256 id, address to)
                  external
                  returns (uint256 newId)
              {
                  bool metaTx = _metaTransactionContracts[msg.sender];
                  require(
                      msg.sender == sender ||
                          metaTx ||
                          _superOperators[msg.sender] ||
                          _operatorsForAll[sender][msg.sender],
                      "require meta approval"
                  );
                  return _extractERC721From(metaTx ? sender : msg.sender, sender, id, to);
              }
              function _extractERC721From(address operator, address sender, uint256 id, address to)
                  internal
                  returns (uint256 newId)
              {
                  require(to != address(0), "destination is zero address");
                  require(id & IS_NFT == 0, "Not an ERC1155 Token");
                  uint32 tokenCollectionIndex = _nextCollectionIndex[id];
                  newId = id +
                      IS_NFT +
                      (tokenCollectionIndex) *
                      2**NFT_INDEX_OFFSET;
                  _nextCollectionIndex[id] = tokenCollectionIndex + 1;
                  _burnERC1155(operator, sender, id, 1);
                  _mint(
                      _metadataHash[id & URI_ID],
                      1,
                      0,
                      operator,
                      to,
                      newId,
                      "",
                      true
                  );
                  emit Extraction(id, newId);
              }
          }
          pragma solidity ^0.5.2;
          contract Admin {
              address internal _admin;
              event AdminChanged(address oldAdmin, address newAdmin);
              /// @notice gives the current administrator of this contract.
              /// @return the current administrator of this contract.
              function getAdmin() external view returns (address) {
                  return _admin;
              }
              /// @notice change the administrator to be `newAdmin`.
              /// @param newAdmin address of the new administrator.
              function changeAdmin(address newAdmin) external {
                  require(msg.sender == _admin, "only admin can change admin");
                  emit AdminChanged(_admin, newAdmin);
                  _admin = newAdmin;
              }
              modifier onlyAdmin() {
                  require (msg.sender == _admin, "only admin allowed");
                  _;
              }
          }
          pragma solidity ^0.5.2;
          import "./Admin.sol";
          contract SuperOperators is Admin {
              mapping(address => bool) internal _superOperators;
              event SuperOperator(address superOperator, bool enabled);
              /// @notice Enable or disable the ability of `superOperator` to transfer tokens of all (superOperator rights).
              /// @param superOperator address that will be given/removed superOperator right.
              /// @param enabled set whether the superOperator is enabled or disabled.
              function setSuperOperator(address superOperator, bool enabled) external {
                  require(
                      msg.sender == _admin,
                      "only admin is allowed to add super operators"
                  );
                  _superOperators[superOperator] = enabled;
                  emit SuperOperator(superOperator, enabled);
              }
              /// @notice check whether address `who` is given superOperator rights.
              /// @param who The address to query.
              /// @return whether the address has superOperator rights.
              function isSuperOperator(address who) public view returns (bool) {
                  return _superOperators[who];
              }
          }
          pragma solidity ^0.5.2;
          /**
              @title ERC-1155 Multi Token Standard
              @dev See https://eips.ethereum.org/EIPS/eip-1155
              Note: The ERC-165 identifier for this interface is 0xd9b67a26.
           */
          interface ERC1155 {
              event TransferSingle(
                  address indexed operator,
                  address indexed from,
                  address indexed to,
                  uint256 id,
                  uint256 value
              );
              event TransferBatch(
                  address indexed operator,
                  address indexed from,
                  address indexed to,
                  uint256[] ids,
                  uint256[] values
              );
              event ApprovalForAll(
                  address indexed owner,
                  address indexed operator,
                  bool approved
              );
              event URI(string value, uint256 indexed id);
              /**
                  @notice Transfers `value` amount of an `id` from  `from` to `to`  (with safety call).
                  @dev Caller must be approved to manage the tokens being transferred out of the `from` account (see "Approval" section of the standard).
                  MUST revert if `to` is the zero address.
                  MUST revert if balance of holder for token `id` is lower than the `value` sent.
                  MUST revert on any other error.
                  MUST emit the `TransferSingle` event to reflect the balance change (see "Safe Transfer Rules" section of the standard).
                  After the above conditions are met, this function MUST check if `to` is a smart contract (e.g. code size > 0). If so, it MUST call `onERC1155Received` on `to` and act appropriately (see "Safe Transfer Rules" section of the standard).
                  @param from    Source address
                  @param to      Target address
                  @param id      ID of the token type
                  @param value   Transfer amount
                  @param data    Additional data with no specified format, MUST be sent unaltered in call to `onERC1155Received` on `to`
              */
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 id,
                  uint256 value,
                  bytes calldata data
              ) external;
              /**
                  @notice Transfers `values` amount(s) of `ids` from the `from` address to the `to` address specified (with safety call).
                  @dev Caller must be approved to manage the tokens being transferred out of the `from` account (see "Approval" section of the standard).
                  MUST revert if `to` is the zero address.
                  MUST revert if length of `ids` is not the same as length of `values`.
                  MUST revert if any of the balance(s) of the holder(s) for token(s) in `ids` is lower than the respective amount(s) in `values` sent to the recipient.
                  MUST revert on any other error.
                  MUST emit `TransferSingle` or `TransferBatch` event(s) such that all the balance changes are reflected (see "Safe Transfer Rules" section of the standard).
                  Balance changes and events MUST follow the ordering of the arrays (_ids[0]/_values[0] before _ids[1]/_values[1], etc).
                  After the above conditions for the transfer(s) in the batch are met, this function MUST check if `to` is a smart contract (e.g. code size > 0). If so, it MUST call the relevant `ERC1155TokenReceiver` hook(s) on `to` and act appropriately (see "Safe Transfer Rules" section of the standard).
                  @param from    Source address
                  @param to      Target address
                  @param ids     IDs of each token type (order and length must match _values array)
                  @param values  Transfer amounts per token type (order and length must match _ids array)
                  @param data    Additional data with no specified format, MUST be sent unaltered in call to the `ERC1155TokenReceiver` hook(s) on `to`
              */
              function safeBatchTransferFrom(
                  address from,
                  address to,
                  uint256[] calldata ids,
                  uint256[] calldata values,
                  bytes calldata data
              ) external;
              /**
                  @notice Get the balance of an account's tokens.
                  @param owner  The address of the token holder
                  @param id     ID of the token
                  @return        The _owner's balance of the token type requested
               */
              function balanceOf(address owner, uint256 id)
                  external
                  view
                  returns (uint256);
              /**
                  @notice Get the balance of multiple account/token pairs
                  @param owners The addresses of the token holders
                  @param ids    ID of the tokens
                  @return        The _owner's balance of the token types requested (i.e. balance for each (owner, id) pair)
               */
              function balanceOfBatch(address[] calldata owners, uint256[] calldata ids)
                  external
                  view
                  returns (uint256[] memory);
              /**
                  @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens.
                  @dev MUST emit the ApprovalForAll event on success.
                  @param operator  Address to add to the set of authorized operators
                  @param approved  True if the operator is approved, false to revoke approval
              */
              function setApprovalForAll(address operator, bool approved) external;
              /**
                  @notice Queries the approval status of an operator for a given owner.
                  @param owner     The owner of the tokens
                  @param operator  Address of authorized operator
                  @return           True if the operator is approved, false if not
              */
              function isApprovedForAll(address owner, address operator)
                  external
                  view
                  returns (bool);
          }
          pragma solidity ^0.5.2;
          /**
              Note: The ERC-165 identifier for this interface is 0x4e2312e0.
          */
          interface ERC1155TokenReceiver {
              /**
                  @notice Handle the receipt of a single ERC1155 token type.
                  @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeTransferFrom` after the balance has been updated.
                  This function MUST return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` (i.e. 0xf23a6e61) if it accepts the transfer.
                  This function MUST revert if it rejects the transfer.
                  Return of any other value than the prescribed keccak256 generated value MUST result in the transaction being reverted by the caller.
                  @param operator  The address which initiated the transfer (i.e. msg.sender)
                  @param from      The address which previously owned the token
                  @param id        The ID of the token being transferred
                  @param value     The amount of tokens being transferred
                  @param data      Additional data with no specified format
                  @return           `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
              */
              function onERC1155Received(
                  address operator,
                  address from,
                  uint256 id,
                  uint256 value,
                  bytes calldata data
              ) external returns (bytes4);
              /**
                  @notice Handle the receipt of multiple ERC1155 token types.
                  @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeBatchTransferFrom` after the balances have been updated.
                  This function MUST return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` (i.e. 0xbc197c81) if it accepts the transfer(s).
                  This function MUST revert if it rejects the transfer(s).
                  Return of any other value than the prescribed keccak256 generated value MUST result in the transaction being reverted by the caller.
                  @param operator  The address which initiated the batch transfer (i.e. msg.sender)
                  @param from      The address which previously owned the token
                  @param ids       An array containing ids of each token being transferred (order and length must match _values array)
                  @param values    An array containing amounts of each token being transferred (order and length must match _ids array)
                  @param data      Additional data with no specified format
                  @return           `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
              */
              function onERC1155BatchReceived(
                  address operator,
                  address from,
                  uint256[] calldata ids,
                  uint256[] calldata values,
                  bytes calldata data
              ) external returns (bytes4);
          }
          pragma solidity ^0.5.2;
          /**
           * @title ERC165
           * @dev https://eips.ethereum.org/EIPS/eip-165
           */
          interface ERC165 {
              /**
             * @notice Query if a contract implements interface `interfaceId`
             * @param interfaceId The interface identifier, as specified in ERC-165
             * @dev Interface identification is specified in ERC-165. This function
             * uses less than 30,000 gas.
             */
              function supportsInterface(bytes4 interfaceId)
                  external
                  view
                  returns (bool);
          }
          pragma solidity ^0.5.2;
          import "./ERC165.sol";
          import "./ERC721Events.sol";
          /**
           * @title ERC721 Non-Fungible Token Standard basic interface
           * @dev see https://eips.ethereum.org/EIPS/eip-721
           */
          /*interface*/
          contract ERC721 is ERC165, ERC721Events {
              function balanceOf(address owner) external view returns (uint256 balance);
              function ownerOf(uint256 tokenId) external view returns (address owner);
              //   function exists(uint256 tokenId) external view returns (bool exists);
              function approve(address to, uint256 tokenId) external;
              function getApproved(uint256 tokenId)
                  external
                  view
                  returns (address operator);
              function setApprovalForAll(address operator, bool approved) external;
              function isApprovedForAll(address owner, address operator)
                  external
                  view
                  returns (bool);
              function transferFrom(address from, address to, uint256 tokenId)
                  external;
              function safeTransferFrom(address from, address to, uint256 tokenId)
                  external;
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 tokenId,
                  bytes calldata data
              ) external;
          }
          pragma solidity ^0.5.2;
          /**
           * @title ERC721 Non-Fungible Token Standard basic interface
           * @dev see https://eips.ethereum.org/EIPS/eip-721
           */
          interface ERC721Events {
              event Transfer(
                  address indexed _from,
                  address indexed _to,
                  uint256 indexed _tokenId
              );
              event Approval(
                  address indexed _owner,
                  address indexed _approved,
                  uint256 indexed _tokenId
              );
              event ApprovalForAll(
                  address indexed _owner,
                  address indexed _operator,
                  bool _approved
              );
          }
          /* This Source Code Form is subject to the terms of the Mozilla Public
           * License, v. 2.0. If a copy of the MPL was not distributed with this
           * file, You can obtain one at http://mozilla.org/MPL/2.0/.
           *
           * This code has not been reviewed.
           * Do not use or deploy this code before reviewing it personally first.
           */
          // solhint-disable-next-line compiler-fixed
          pragma solidity ^0.5.2;
          interface ERC721TokenReceiver {
              function onERC721Received(
                  address operator,
                  address from,
                  uint256 tokenId,
                  bytes calldata data
              ) external returns (bytes4);
          }
          pragma solidity ^0.5.2;
          library AddressUtils {
              function toPayable(address _address) internal pure returns (address payable _payable) {
                  return address(uint160(_address));
              }
              function isContract(address addr) internal view returns (bool) {
                  // for accounts without code, i.e. `keccak256('')`:
                  bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                  bytes32 codehash;
                  // solium-disable-next-line security/no-inline-assembly
                  assembly {
                      codehash := extcodehash(addr)
                  }
                  return (codehash != 0x0 && codehash != accountHash);
              }
          }
          pragma solidity ^0.5.2;
          import "./SafeMathWithRequire.sol";
          library ObjectLib32 {
              using SafeMathWithRequire for uint256;
              enum Operations {ADD, SUB, REPLACE}
              // Constants regarding bin or chunk sizes for balance packing
              uint256 constant TYPES_BITS_SIZE = 32; // Max size of each object
              uint256 constant TYPES_PER_UINT256 = 256 / TYPES_BITS_SIZE; // Number of types per uint256
              //
              // Objects and Tokens Functions
              //
              /**
            * @dev Return the bin number and index within that bin where ID is
            * @param tokenId Object type
            * @return (Bin number, ID's index within that bin)
            */
              function getTokenBinIndex(uint256 tokenId)
                  internal
                  pure
                  returns (uint256 bin, uint256 index)
              {
                  bin = (tokenId * TYPES_BITS_SIZE) / 256;
                  index = tokenId % TYPES_PER_UINT256;
                  return (bin, index);
              }
              /**
            * @dev update the balance of a type provided in binBalances
            * @param binBalances Uint256 containing the balances of objects
            * @param index Index of the object in the provided bin
            * @param amount Value to update the type balance
            * @param operation Which operation to conduct :
            *     Operations.REPLACE : Replace type balance with amount
            *     Operations.ADD     : ADD amount to type balance
            *     Operations.SUB     : Substract amount from type balance
            */
              function updateTokenBalance(
                  uint256 binBalances,
                  uint256 index,
                  uint256 amount,
                  Operations operation
              ) internal pure returns (uint256 newBinBalance) {
                  uint256 objectBalance = 0;
                  if (operation == Operations.ADD) {
                      objectBalance = getValueInBin(binBalances, index);
                      newBinBalance = writeValueInBin(
                          binBalances,
                          index,
                          objectBalance.add(amount)
                      );
                  } else if (operation == Operations.SUB) {
                      objectBalance = getValueInBin(binBalances, index);
                      require(objectBalance >= amount, "can't substract more than there is");
                      newBinBalance = writeValueInBin(
                          binBalances,
                          index,
                          objectBalance.sub(amount)
                      );
                  } else if (operation == Operations.REPLACE) {
                      newBinBalance = writeValueInBin(binBalances, index, amount);
                  } else {
                      revert("Invalid operation"); // Bad operation
                  }
                  return newBinBalance;
              }
              /*
            * @dev return value in binValue at position index
            * @param binValue uint256 containing the balances of TYPES_PER_UINT256 types
            * @param index index at which to retrieve value
            * @return Value at given index in bin
            */
              function getValueInBin(uint256 binValue, uint256 index)
                  internal
                  pure
                  returns (uint256)
              {
                  // Mask to retrieve data for a given binData
                  uint256 mask = (uint256(1) << TYPES_BITS_SIZE) - 1;
                  // Shift amount
                  uint256 rightShift = 256 - TYPES_BITS_SIZE * (index + 1);
                  return (binValue >> rightShift) & mask;
              }
              /**
            * @dev return the updated binValue after writing amount at index
            * @param binValue uint256 containing the balances of TYPES_PER_UINT256 types
            * @param index Index at which to retrieve value
            * @param amount Value to store at index in bin
            * @return Value at given index in bin
            */
              function writeValueInBin(uint256 binValue, uint256 index, uint256 amount)
                  internal
                  pure
                  returns (uint256)
              {
                  require(
                      amount < 2**TYPES_BITS_SIZE,
                      "Amount to write in bin is too large"
                  );
                  // Mask to retrieve data for a given binData
                  uint256 mask = (uint256(1) << TYPES_BITS_SIZE) - 1;
                  // Shift amount
                  uint256 leftShift = 256 - TYPES_BITS_SIZE * (index + 1);
                  return (binValue & ~(mask << leftShift)) | (amount << leftShift);
              }
          }
          pragma solidity ^0.5.2;
          /**
           * @title SafeMath
           * @dev Math operations with safety checks that revert
           */
          library SafeMathWithRequire {
              /**
              * @dev Multiplies two numbers, throws on overflow.
              */
              function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
                  // Gas optimization: this is cheaper than asserting 'a' not being zero, but the
                  // benefit is lost if 'b' is also tested.
                  // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
                  if (a == 0) {
                      return 0;
                  }
                  c = a * b;
                  require(c / a == b, "overflow");
                  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) {
                  require(b <= a, "undeflow");
                  return a - b;
              }
              /**
              * @dev Adds two numbers, throws on overflow.
              */
              function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
                  c = a + b;
                  require(c >= a, "overflow");
                  return c;
              }
          }