ETH Price: $3,064.25 (-1.83%)

Token

EET (EET)
 

Overview

Max Total Supply

375 EET

Holders

160

Market

Volume (24H)

N/A

Min Price (24H)

N/A

Max Price (24H)

N/A
Filtered by Token Holder
douglassmuseum.eth
Balance
3 EET
0xa0393a76b132526a70450273cafeceb45eea6dee
Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information
# Exchange Pair Price  24H Volume % Volume

Contract Source Code Verified (Exact Match)

Contract Name:
EET

Compiler Version
v0.8.14+commit.80d49f37

Optimization Enabled:
No with 100 runs

Other Settings:
default evmVersion
File 1 of 18 : EET.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;


import "./GIF89a.sol";
import "./BytesLib.sol";
import "./iGUA.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface iBondingCurve {
  function getFee(uint256 _amount, address _currency) external view returns (uint256 fee);
  function burnTo(uint256 _tokenId, address _owner, address payable _msgSender, address _currency, bytes memory _burnPayload) external returns (bool rewarded);
  function pay(address _payee, uint256 _amount, uint256 _tokenCount, address _currency, bytes memory _mintPayload) external payable returns(bool success);
}

interface iEETRenderEngine {
  function render(uint256 _tokenId, address _guaContract, bytes3[] memory _colors, bytes memory _packedHeader) external view returns (string memory);
  function api(uint256 _tokenId, address _guaContract, bytes3[] memory _colors, bytes memory _packedHeader, address _scoreBoardAddress) external view returns (string memory json);
}

interface iScoreBoard {
  function addMintPayload(uint256 _eetTokenId, address _msgSender, bytes memory _mintPayload) external;
  function addBurnPayload(uint256 _eetTokenId, address _msgSender, bytes memory _burnPayload) external;
}


/** @title EET Contract
  * @author @0xAnimist
  * @notice A collaboration between Cai Guo-Qiang and Kanon
  */
contract EET is ERC721Enumerable, ReentrancyGuard {
  address public _artist;
  address public _manager;
  bytes3[] public _colors;
  bool public _colorsFrozen = false;

  uint256 private _ww = 9;
  uint256 private _hh = 15;
  address public _guaContract;

  bytes1 private _transIndex = 0x00;
  bytes1 private _disposalMethod = 0x04;
  bytes2 private _delay = 0x6400;

  address public _BondingCurveAddress;
  address public _EETRenderEngineAddress;
  address public _scoreBoardAddress;
  bool public _depsFrozen = false;

  struct GIFparts {
    bytes packedHeader;
    bytes header;
    bytes gce;
    bytes trailer;
  }

  GIFparts private _gifParts;

  modifier onlyAuth(){
    require(msg.sender == _manager || msg.sender == _artist, "a");
    _;
  }

  /**
    * @dev Constructor
    * @param manager_ Address Address of manager
    */
  constructor(address manager_, bytes3[] memory colors_) ERC721("EET", "EET"){
    _artist = msg.sender;
    _manager = manager_;
    _updateColors(colors_);
  }


  /**
    * @dev Updates colors
    * @param colors_ Array of colors by hex value
    */
  function _updateColors(bytes3[] memory colors_) internal onlyAuth {
    _colors = colors_;

    //Generate GIF parts
    bytes1 packedLSD = GIF89a.formatLSDPackedField(_colors);
    bytes memory header = BytesLib.concat(BytesLib.concat(GIF89a.formatHeader(), GIF89a.formatLSD(_ww, _hh, packedLSD)), GIF89a.formatGCT(_colors));
    bytes memory aeb = GIF89a.formatAEB(uint16(1));
    bytes memory gce = GIF89a.formatGCE(true, _disposalMethod, _delay, true, _transIndex);
    bytes memory trailer = GIF89a.formatTrailer();

    bytes memory packedHeader = BytesLib.concat(header, BytesLib.concat(aeb, gce));

    _gifParts = GIFparts(packedHeader, header, gce, trailer);
  }


  /**
    * @dev Sets contract manager
    * @param manager_ Address Address of manager
    */
  function setManager(address manager_) public {
    require(msg.sender == _manager, "m");
    _manager = manager_;
  }

  /**
    * @dev Sets contract dependencies
    * @param guaContract_ Address of GUA contract
    * @param BondingCurveAddress_ Address of BondingCurve contract
    * @param EETRenderEngineAddress_ Address of EETRenderEngine contract
    * @param ScoreBoardAddress_ Address of ScoreBoard contract
    * @param _freeze Blocks any future changes if true
    */
  function setDependencies(address guaContract_, address BondingCurveAddress_, address EETRenderEngineAddress_, address ScoreBoardAddress_, bool _freeze) external onlyAuth {
    require(!_depsFrozen, "f");
    _guaContract = guaContract_;
    _BondingCurveAddress = BondingCurveAddress_;
    _EETRenderEngineAddress = EETRenderEngineAddress_;
    _scoreBoardAddress = ScoreBoardAddress_;

    _depsFrozen = _freeze;
  }


  /**
    * @dev Returns the URI of the token
    * @param _tokenId the token
    */
  function tokenURI(uint256 _tokenId) public view override returns(string memory) {
    return iEETRenderEngine(_EETRenderEngineAddress).render(_tokenId, _guaContract, _colors, _gifParts.packedHeader);
  }

  /**
    * @dev Returns the URI of the token
    * @param _tokenId the token
    */
  function tokenAPI(uint256 _tokenId) public view returns(string memory) {
    return iEETRenderEngine(_EETRenderEngineAddress).api(_tokenId, _guaContract, _colors, _gifParts.packedHeader, _scoreBoardAddress);
  }


  /**
    * @dev Mints one or more EET NFTs and corresponding GUA NFTs
    * @param _owners array of addresses to which the tokens will be minted
    * @param _queryhash keccak256 hash of the query
    * @param _rand Random number representing context/intent
    */
  function mint(address[] memory _owners, bytes32[] memory _queryhash, uint256[] memory _rand, address _currency, string[] memory _encrypteds, bytes memory _mintPayload) public payable nonReentrant returns (uint256[] memory tokenIds){
    //Pay fee
    uint256 fee = iBondingCurve(_BondingCurveAddress).getFee(_queryhash.length, _currency);

    require(iBondingCurve(_BondingCurveAddress).pay{value:msg.value, gas: gasleft()}(msg.sender, fee, _queryhash.length, _currency, _mintPayload), "u");

    //Mint tokens
    tokenIds = new uint256[](_queryhash.length);

    for(uint256 i = 0; i < _queryhash.length; i++){
      //mint GUA nft
      (tokenIds[i],) = iGUA(_guaContract).mint(_BondingCurveAddress, _queryhash[i], _rand[i], _encrypteds[i]);
      //store payload
      iScoreBoard(_scoreBoardAddress).addMintPayload(tokenIds[i], msg.sender, _mintPayload);

      //mint eet nft
      _safeMint(_owners[i], tokenIds[i]);//starts at token 1 bc seed starts at token 1
    }
  }


  /**
    * @dev Burns EET NFT and unlocks GUA NFT for a reward from the Bonding Curve
    * @param _tokenIds Tokens to burn
    * @param _currency Currency of the reward
    * @param _burnPayload data
    */
  function burnToCurve(uint256[] memory _tokenIds, address _currency, bytes memory _burnPayload) public nonReentrant {
    for(uint256 i = 0; i < _tokenIds.length; i++){
      address owner = ownerOf(_tokenIds[i]);
      _burnToken(_tokenIds[i], msg.sender, _burnPayload);

      require(iBondingCurve(_BondingCurveAddress).burnTo(_tokenIds[i], owner, payable(msg.sender), _currency, _burnPayload), "f");
    }
  }

  /**
    * @dev Burns EET NFT and updates ScoreBoard
    * @param _tokenId Token to burn
    * @param _msgSender sender
    * @param _burnPayload data
    */
  function _burnToken(uint256 _tokenId, address _msgSender, bytes memory _burnPayload) internal {
    require(_isApprovedOrOwner(_msgSender, _tokenId), "a");
    _burn(_tokenId);

    iScoreBoard(_scoreBoardAddress).addBurnPayload(_tokenId, msg.sender, _burnPayload);
  }

}//end

File 2 of 18 : iGUA.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/** @title GUA Interface
  * @author @0xAnimist
  * @notice A collaboration between Cai Guo-Qiang and Kanon
  */
interface iGUA {
  function getData(uint256 _tokenId) external view returns(bytes memory, bytes32 seed, bool queried, string memory encrypted);

  //function getGifs() external view returns(bytes[] memory);

  function tokenAPI(uint256 _tokenId) external view returns(string memory);

  function mint(address _owner, bytes32 _queryhash, uint256 _rand, string memory _encrypted) external returns(uint256 tokenId, bytes32 seed);

  function publishQuery(uint256 _tokenId, string memory _query) external returns (bool published);

  function redeemFortune(uint256 _tokenId, bytes32 _queryhash, uint256 _rand, string memory _encrypted) external returns(bool success);
}//end

File 3 of 18 : GIF89a.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.0 <0.9.0;

import "./BytesLib.sol";

/** @title GIF89a Contract
  * @author @0xAnimist
  * @notice First Onchain GIF, collaboration between Cai Guo-Qiang and Kanon
  */
library GIF89a {

  bytes1 public constant IMAGE_SEPARATOR = 0x2c;


  function buildStaticGIF(bytes3[] memory _colors, uint256 _ww, uint256 _hh, bool _trans, bytes1 _transIndex, uint8[][] memory _frame, bytes1 _packedLSD, uint16 _minCodeSize) public pure returns (bytes memory gif) {
    gif = formatHeader();
    gif = bytes.concat(gif, formatLSD(_ww, _hh, _packedLSD));
    gif = bytes.concat(gif, formatGCT(_colors));
    gif = bytes.concat(gif, formatGCE(false, 0x00, 0x0000, _trans, _transIndex));

    bytes1 packedImgDesc = 0x00;//no local color tables used
    gif = bytes.concat(gif, formatImageDescriptor(0, 0, _ww, _hh, packedImgDesc));

    gif = bytes.concat(
      gif,
      formatImageLZW(
        _frame,
        _minCodeSize
      )
    );

    gif = bytes.concat(gif, formatTrailer());
  }


  function buildAnimatedGifByPixel(bytes memory _buffer, uint8 _i, uint8 _y, bytes memory _packedHeader, bytes memory _gce, bytes memory _pixel) public pure returns (bytes memory buffer){
    //image descriptor
    bytes memory imgDesc = formatImageDescriptor(_i, _y, 3, 3, 0x0000);

    //pixel-specific metadata
    if(_i == 0){//if first iteration
      buffer = BytesLib.concat(_packedHeader, imgDesc);
    }else{
      buffer = BytesLib.concat(_buffer, BytesLib.concat(_gce, imgDesc));
    }

    //lzw image data
    buffer = BytesLib.concat(buffer, _pixel);
  }



  function assembleGIFStack(bytes[] memory _parts) public pure returns (bytes memory gif) {

    for(uint256 i = 0; i < _parts.length; i++){
      gif = BytesLib.concat(gif, _parts[i]);
    }
  }

  function assembleHeader(bytes3[] memory _colors, uint256 _ww, uint256 _hh) public pure returns (bytes[] memory header) {
    header = new bytes[](3);

    //format Header
    header[0] = formatHeader();

    //format LSD
    bytes1 packedLSD = formatLSDPackedField(_colors);
    header[1] = formatLSD(_ww, _hh, packedLSD);

    //format GCT
    header[2] = formatGCT(_colors);
  }


  function assembleGIF(bytes memory _header, bytes memory _lsd, bytes memory _gct, bytes memory _gce, bytes memory _imgDesc, bytes memory _img, bytes memory _trailer) public pure returns (bytes memory) {
    bytes memory buffer;
    assembly {
      buffer := mload(0x40)//buffer == 0x80

      //total length
      let len := add(mload(_gce),add(mload(_trailer),add(mload(_img),add(mload(_imgDesc),add(mload(_gct),add(mload(_lsd),mload(_header)))))))
      mstore(buffer, len)

      //header
      let pointer := add(buffer,0x20)//store the data after the length word
      let headerData := mload(add(_header,0x20))
      mstore(pointer, headerData)

      //lsd
      pointer := add(pointer,mload(_header))//offset by header length
      let lsdData := mload(add(_lsd,0x20))
      mstore(pointer, lsdData)

      pointer := add(pointer,mload(_lsd))
      let gctData := mload(add(_gct,0x20))
      mstore(pointer, gctData)

      pointer := add(pointer,mload(_gct))
      let gceData := mload(add(_gce,0x20))
      mstore(pointer, gceData)

      pointer := add(pointer,mload(_gce))
      let imgDescData := mload(add(_imgDesc,0x20))
      mstore(pointer, imgDescData)

      pointer := add(pointer,mload(_imgDesc))


      let datawords := div(mload(_img),0x20)//number of 32-byte words of img data

      for { let i := 1 } lt(i, add(datawords,1)) { i := add(i, 1) } {
        mstore(pointer,mload(add(_img,mul(i,0x20))))
        pointer := add(pointer,0x20)
      }

      //store remainder of _img
      let rem := mod(mload(_img),32)//3

      for { let i := 0 } lt(i, rem) { i := add(i, 1) } {
        mstore8(pointer, byte(i,mload(add(_img,mul(add(datawords,1),0x20)))))
        pointer := add(pointer,1)
      }

      let trailerData := mload(add(_trailer,0x20))
      mstore(pointer, trailerData)

      //determine how many 32-byte words are used in total
      let words := div(len,0x20)//total 32-byte words
      if gt(mod(len,32), 0) { words := add(words,1) }

      //update free memory pointer
      let nextmem := add(add(buffer,0x20),mul(words,0x20))
      mstore(0x40, nextmem)
    }
    return buffer;
  }

  //Global Color Table
  function formatGCT(bytes3[] memory _colors) public pure returns (bytes memory) {
    require(_colors.length <= 256, "GIF89a: exceeds max colors");

    uint256 len = fullColorTableSize(_colors.length);
    bytes memory buffer;
    bytes3 empty = 0x000000;

    //fill gct with all colors
    for(uint256 i = 0; i < _colors.length; i++){
      buffer = bytes.concat(buffer, _colors[i]);
    }//end for i

    //pad gct so size is 2^n
    for(uint256 i = _colors.length; i < len; i++){
      buffer = bytes.concat(buffer, empty);
    }

    return buffer;
  }

  //GIF89a
  function formatHeader() public pure returns (bytes memory) {
    bytes memory buffer = new bytes(6);

    buffer[0] = 0x47;//G
    buffer[1] = 0x49;//I
    buffer[2] = 0x46;//F
    buffer[3] = 0x38;//8
    buffer[4] = 0x39;//9
    buffer[5] = 0x61;//a

    return buffer;
  }

  //Logical Screen Display Packed Field
  function formatLSDPackedField(bytes3[] memory _colors) public pure returns(bytes1) {
    bytes memory packedField;

    uint256 gctSize = fullColorTableSize(_colors.length);
    uint8 colorResolution = uint8(root2(gctSize) - 1);

    require(colorResolution >= 0 && colorResolution < 8, "GIF89a: color resolution out of bounds");

    assembly {
      packedField := mload(0x40)
      mstore(packedField, 1)
      let pointer := add(packedField, 0x20)
      mstore8(pointer, or(or(0x80, colorResolution), shl(4,colorResolution)))//0x80 for global color table flag
      mstore(0x40, 0x21)//TODO: should be add(packedField, 0x21) i think!?
    }

    return bytes1(packedField);
  }

  //Logical Screen Display
  function formatLSD(uint256 _ww, uint256 _hh, bytes1 _packedField) public pure returns (bytes memory) {
    bytes memory buffer;// = new bytes(6);

    assembly {
      buffer := mload(0x40)//buffer == 0x80

      mstore(buffer, 0x07)//length == 0x06 == 6

      let dataPointer := add(buffer, 0x20)//store the data after the length word

      //full image width
      mstore8(dataPointer, byte(31,_ww))
      mstore8(add(dataPointer,1), byte(30,_ww))

      //full image height
      mstore8(add(dataPointer,2), byte(31,_hh))
      mstore8(add(dataPointer,3), byte(30,_hh))

      //packed field
      mstore8(add(dataPointer,4), byte(0,_packedField))

      //background color index
      mstore8(add(dataPointer,5), 0x00)

      //pixel aspect ratio (likely not implemented)
      mstore8(add(dataPointer,6), 0x00)

      mstore(0x40, add(buffer, 0x40))//0xc0
    }

    return buffer;
  }

  //Application Extension Block (for infinite loop animation)
  function formatAEB(uint16 _loops) public pure returns (bytes memory) {
    bytes memory buffer = new bytes(19);

    bytes memory loops = abi.encodePacked(_loops);

    buffer[0] = 0x21;//GIF extension code
    buffer[1] = 0xFF;//Application extension label
    buffer[2] = 0x0B;//Length of Application Block
    buffer[3] = 0x4E;//"N"
    buffer[4] = 0x45;//"E"
    buffer[5] = 0x54;//"T"
    buffer[6] = 0x53;//"S"
    buffer[7] = 0x43;//"C"
    buffer[8] = 0x41;//"A"
    buffer[9] = 0x50;//"P"
    buffer[10] = 0x45;//"E"
    buffer[11] = 0x32;//"2"
    buffer[12] = 0x2E;//"."
    buffer[13] = 0x30;//"0"
    buffer[14] = 0x03;//Length of data sub-blocks
    buffer[15] = 0x01;//convention 0x01
    buffer[16] = loops[0];//0x01;//Little endian # of loops: loop only 1x
    buffer[17] = loops[1];//0x00;//^^
    buffer[18] = 0x00;//Data sub-block terminator

    return buffer;
  }


  /**
    * @dev Graphics Control Extension
    * @param _disposal 0x04 if you want to leave the last frame in place after the animation has finished; 0x08 if you want the last frame to be just the background color
    * @param _transIndex numerical gct index of the transparent color in bytes1 format
   */
  function formatGCE(bool _animated, bytes1 _disposal, bytes2 _delay, bool _transparent, bytes1 _transIndex) public pure returns (bytes memory) {
    bytes memory buffer = new bytes(8);

    buffer[0] = 0x21;
    buffer[1] = 0xf9;
    buffer[2] = 0x04;
    buffer[3] = _animated ? _disposal : bytes1(0x00);
    if(_transparent){
      buffer[3] = buffer[3] | bytes1(0x01);
    }
    buffer[4] = _animated ? _delay[0] : bytes1(0x00);
    buffer[5] = _animated ? _delay[1] : bytes1(0x00);
    buffer[6] = _transparent ? _transIndex : bytes1(0x00);
    buffer[7] = 0x00;

    return buffer;
  }

  /**
    * @dev Image Descriptor
    * @param _ll image left
    * _@param _tt image top
    */
  function formatImageDescriptor(uint256 _ll, uint256 _tt, uint256 _ww, uint256 _hh, bytes1 _packedField) public pure returns (bytes memory) {
    bytes memory buffer;

    assembly {
      buffer := mload(0x40)//buffer == 0x80

      mstore(buffer, 0x0a)//length == 0xa0 == 10

      let dataPointer := add(buffer, 0x20)//store the data after the length word

      mstore8(dataPointer, 0x2c)//byte(0,IMAGE_SEPARATOR))

      //image left
      mstore8(add(dataPointer,1), byte(31,_ll))
      mstore8(add(dataPointer,2), byte(30,_ll))

      //image top
      mstore8(add(dataPointer,3), byte(31,_tt))
      mstore8(add(dataPointer,4), byte(30,_tt))

      //full image width
      mstore8(add(dataPointer,5), byte(31,_ww))
      mstore8(add(dataPointer,6), byte(30,_ww))

      //full image height
      mstore8(add(dataPointer,7), byte(31,_hh))
      mstore8(add(dataPointer,8), byte(30,_hh))

      //packed field
      mstore8(add(dataPointer,9), byte(0,_packedField))

      mstore(0x40, add(buffer, 0x40))//0xc0
    }

    return buffer;
  }

  //Trailer
  function formatTrailer() public pure returns(bytes memory) {
    bytes memory trailer = new bytes(1);
    trailer[0] = 0x3b;
    return trailer;
  }

  ////////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////////
  /////////////////////////////////// IMAGE //////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////////

  /**
   * @dev Format Image in LZW Compression
   * @param _minimumCodeSize bits/pixel required (thus, size of gct == 2**_minimumCodeSize)
   */
  function formatImageLZW(uint8[][] memory _indexMatrix, uint16 _minimumCodeSize) public pure returns (bytes memory) {
    //convert index matrix (im) to index stream (is)
    uint256 width = _indexMatrix[0].length;
    uint256 totalIndices = _indexMatrix.length * width;// assumes a rectangular index matrix
    bytes memory indexStream = new bytes(totalIndices);//each value is [0,0xff] == [0,255] because |gct| >= 256
    for(uint256 i = 0; i <  _indexMatrix.length; i++){
      for(uint256 j = 0; j < width; j++){
        indexStream[(i*width)+j] =  bytes1(_indexMatrix[i][j]);
      }
    }

    //generate code stream (cs)
    bytes memory cs = encodeImage(indexStream, _minimumCodeSize);

    //break code stream down into chunks <= 0xff in length
    cs = chunkCodeStream(cs);

    //prepend minimum code size
    cs = bytes.concat(bytes1(uint8(_minimumCodeSize)), cs);

    return cs;
  }

  //this function chunks the code stream out into N 0xff-long blocks
  function chunkCodeStream(bytes memory _cs) public pure returns(bytes memory cs) {
    uint256 fullChunks = (_cs.length / 0xff);
    uint8 remainder = uint8(_cs.length % 0xff);
    uint256 chunks = (remainder > 0) ? fullChunks + 1 : fullChunks;

    cs = new bytes(_cs.length + 2*chunks);

    uint256 i = 0;
    uint256 j = 0;
    while(remainder > 0){
      if(fullChunks > 0){
        cs[i++] = 0xff;
        for(uint256 k = j; k < (j+256); k++){
          cs[i++] = _cs[k];
        }
        cs[i++] = 0x00;
        j += 256;
        fullChunks--;
      }else{
        cs[i++] = bytes1(remainder);
        for(uint256 k = j; k < (j + uint256(remainder)); k++){
          cs[i++] = _cs[k];
        }
        cs[i] = 0x00;
        remainder = 0;
      }
    }//end while
  }


  function encodeImage(bytes memory _is, uint16 _minimumCodeSize) public pure returns(bytes memory) {
    uint16 codeSizeInBits = _minimumCodeSize + 1;
    (bytes memory cs, int256 isIndex) = packImage(_is, codeSizeInBits);
    while(isIndex < 0){
      _is = removeFirstNBytes(_is, uint256(isIndex*(-1)));
      (cs, isIndex) = packImage(_is, codeSizeInBits);
    }
    return cs;
  }

  function removeFirstNBytes(bytes memory _is, uint256 _n) public pure returns(bytes memory is_) {
    is_ = new bytes(_is.length - _n);
    for(uint256 j = _n; j < is_.length; j++){
      is_[j-_n] = _is[j];
    }
  }

  /**
   * @param _codeSizeInBits initial code size, one greater than the minimum code size (ie. one bit greater than the amount needed to represent all the indices in the gct)
   */
  function packImage(bytes memory _is, uint16 _codeSizeInBits) public pure returns(bytes memory cs, int isIndex) {
    uint256 csBitLen = 0;
    uint16 cc = uint16(2**(_codeSizeInBits-1));

    bytes[] memory ct;//code table
    (cs, csBitLen) = addToCS(cs, csBitLen, cc, _codeSizeInBits);//send clear code (== total colors == 2**_minimumCodeSize == 2**(_codeSizeInBits-1))

    bytes memory ib = new bytes(1);//index buffer
    ib[0] = _is[uint256(isIndex++)];

    uint256 index;
    uint256 code;
    for(uint256 i = uint256(isIndex); i < _is.length; i++){
      ib = pushToBytes(ib, _is[i]);

      //emit IB(i, ib);
      bool alreadyInCT;
      (alreadyInCT, index) = isInCodeTable(ct, ib, cc+1);

      if(!alreadyInCT){
        if(ib.length == 2){
          (cs, csBitLen) = addToCS(cs, csBitLen, uint256(uint8(ib[0])), _codeSizeInBits);
        }else{
          (cs, csBitLen) = addToCS(cs, csBitLen, code, _codeSizeInBits);
        }

        //add ib to code table, increment codeSizeInBits if appropriate
        (ct, _codeSizeInBits) = addToCT(ct, ib, _codeSizeInBits, (cc+1));

        ib = clearToLen1(ib);
        ib[0] = _is[i];

        //push ib[0] to the code stream if this is the last index
        if(i == (_is.length-1)){
          (cs, csBitLen) = addToCS(cs, csBitLen, uint256(uint8(ib[0])), _codeSizeInBits);
        }

        //must reset color table (ct) if
        if(ct.length == (4095 - cc - 1)){
          isIndex = int(i+1)*(-1);//i has been added to the cs, so start again at i+1
          break;
        }

      }else{
        code = index;
        //push code to the code stream if this is the last index
        if(i == (_is.length-1)){
          (cs, csBitLen) = addToCS(cs, csBitLen, code, _codeSizeInBits);
        }
      }
    }//end for

    //(cs, csBitLen) = addToCS(cs, csBitLen, index, _codeSizeInBits);
    (cs,) = addToCS(cs, csBitLen, cc+1, _codeSizeInBits);//_totalColors + 1 == end of information code
  }


  function invertByteOrder(bytes memory _cs) public pure returns(bytes memory cs) {
    cs = new bytes(_cs.length);
    for(uint256 i = 0; i < _cs.length; i++){
      cs[i] = _cs[_cs.length - 1];
    }
  }

  function addToCS(bytes memory _cs, uint256 _csBitLen, uint256 _code, uint256 _codeSizeInBits) public pure returns(bytes memory cs, uint256 csBitLen) {
    uint256 bitsUsedInLastByte = _csBitLen % 8;//how many used bits in the last byte
    uint256 bitsLeftInLastByte = 8 - bitsUsedInLastByte;
    uint256 bytesToChange = 0;
    uint256 bytesToAdd = 0;

    if(bitsUsedInLastByte == 0){
      bytesToAdd = (_codeSizeInBits > 8) ? 2 : 1;
    }else{
      bytesToChange = 1;
      if(_codeSizeInBits > bitsLeftInLastByte){
        bytesToAdd++;
        if(_codeSizeInBits > (8 + bitsLeftInLastByte)){
          bytesToAdd++;
        }//end if
      }//end if
    }//end ifelse

    if(bytesToChange == 1){
      assembly {
        let lastByteOfCSPointer := add(_cs,add(0x20,sub(mload(_cs),1)))
        let lastByteOfCS := byte(0, mload(lastByteOfCSPointer))
        let oredLastByte := or(lastByteOfCS, byte(31,shl(bitsUsedInLastByte, _code)))//0x0c
        mstore8(lastByteOfCSPointer, oredLastByte)
      }//end assembly
    }//end if

    cs = new bytes(_cs.length + bytesToAdd);
    for(uint256 i = 0; i < _cs.length; i++){
      cs[i] = _cs[i];
    }//end for

    if(bytesToAdd > 0){
      assembly {
        let firstNewByteOfCSPointer := add(cs,add(0x20, mload(_cs)))
        mstore8(firstNewByteOfCSPointer, byte(sub(31,bytesToChange),shl(bitsUsedInLastByte, _code)))

        if eq(bytesToAdd, 2) {
          mstore8(add(firstNewByteOfCSPointer, 1), byte(sub(30,bytesToChange),shl(bitsUsedInLastByte, _code)))
        }//end if
      }//end assembly
    }//end if

    csBitLen = _csBitLen + _codeSizeInBits;
  }

  function clearToLen1(bytes memory _arr) public pure returns(bytes memory) {
    bytes memory arr = new bytes(1);
    for(uint256 i = 0; i < _arr.length-1; i++){
      delete _arr[i];
    }
    _arr = arr;
    return arr;
  }

  function push1DTo(uint256[] memory _pre, uint256[] memory _post) public pure returns(uint256[] memory arr) {
    uint256 len = _pre.length + _post.length;
    arr = new uint256[](len);
    for(uint256 i = 0; i < _pre.length; i++){
      arr[i] = _pre[i];
    }
    for(uint256 j = _pre.length; j < len; j++){
      arr[j] = _post[j-_pre.length];
    }
  }

  function pushTo(uint256[] memory _arr, uint256 _value) public pure returns(uint256[] memory arr) {
    arr = new uint256[](_arr.length+1);
    for(uint256 i = 0; i < _arr.length; i++){
      arr[i] = _arr[i];
    }
    arr[_arr.length] = _value;
  }

  function pushToBytes(bytes memory _arr, bytes1 _value) public pure returns(bytes memory arr) {
    arr = new bytes(_arr.length+1);
    arr = bytes.concat(_arr,_value);
  }

  function popFrom(uint256[] memory _arr) public pure returns(uint256[] memory arr) {
    arr = new uint256[](_arr.length-1);
    for(uint256 i = 0; i < _arr.length-1; i++){
      arr[i] = _arr[i];
    }
  }

  function addToCT(bytes[] memory _ct, bytes memory _arr, uint16 _codeSizeInBits, uint256 _eoi) public pure returns(bytes[] memory ct, uint16 codeSizeInBits) {
    uint256 len = _ct.length+1;
    //increment code size if latest code is == 2**codeSizeInBits - 1
    if((_ct.length + _eoi) >= ((2**_codeSizeInBits) - 1)){
      codeSizeInBits = _codeSizeInBits + 1;
    }else{
      codeSizeInBits = _codeSizeInBits;
    }

    ct = new bytes[](len);
    for(uint256 i = 0; i < len-1; i++){
      ct[i] = _ct[i];
    }

    ct[len-1] = new bytes(_arr.length);
    for(uint256 j = 0; j < _arr.length; j++){
      ct[len-1][j] = _arr[j];
    }
  }

  function isInCodeTable(bytes[] memory _ct, bytes memory _ib, uint256 _eoi) public pure returns(bool contained, uint256 index) {
    //compare ib against every element of _ct
    for(uint256 i = 0; i < _ct.length; i++){
      if(_ct[i].length == _ib.length){
        bool matches = true;
        for(uint256 j = 0; j < _ct[i].length; j++){
          if(_ct[i][j] != _ib[j]){
            matches = false;
            break;
          }
        }

        if(matches){
          return (true, i+_eoi+1);
        }
      }//end if
    }//end for

    return (false,0);
  }

  function root2(uint256 _val) public pure returns(uint256 n) {
    //require(_val%2 == 0, "GIF89a: root2");

    while(_val > 1){
      require(_val%2 == 0, "GIF89a: root2");
      _val = _val/2;
      n++;
    }
  }


  function fullColorTableSize(uint256 _value) public pure returns(uint256 len) {
    len = 1;
    uint256 temp = _value - 1;

    while(temp > 1){
      temp = temp/2;
      len++;
    }

    len = 2**len;
  }

  function getMinimumCodeSize(uint256 _totalColors) public pure returns(uint256 minCodeSize) {
    minCodeSize = root2(fullColorTableSize(_totalColors));
    if(minCodeSize < 2){
      return 2;
    }
  }

}//end GIF89a

File 4 of 18 : BytesLib.sol
// SPDX-License-Identifier: Unlicense
/*
 * @title Solidity Bytes Arrays Utils
 * @author Gonçalo Sá <[email protected]>
 *
 * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
 *      The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
 */
pragma solidity >=0.8.0 <0.9.0;


library BytesLib {
    function concat(
        bytes memory _preBytes,
        bytes memory _postBytes
    )
        internal
        pure
        returns (bytes memory)
    {
        bytes memory tempBytes;

        assembly {
            // Get a location of some free memory and store it in tempBytes as
            // Solidity does for memory variables.
            tempBytes := mload(0x40)

            // Store the length of the first bytes array at the beginning of
            // the memory for tempBytes.
            let length := mload(_preBytes)
            mstore(tempBytes, length)

            // Maintain a memory counter for the current write location in the
            // temp bytes array by adding the 32 bytes for the array length to
            // the starting location.
            let mc := add(tempBytes, 0x20)
            // Stop copying when the memory counter reaches the length of the
            // first bytes array.
            let end := add(mc, length)

            for {
                // Initialize a copy counter to the start of the _preBytes data,
                // 32 bytes into its memory.
                let cc := add(_preBytes, 0x20)
            } lt(mc, end) {
                // Increase both counters by 32 bytes each iteration.
                mc := add(mc, 0x20)
                cc := add(cc, 0x20)
            } {
                // Write the _preBytes data into the tempBytes memory 32 bytes
                // at a time.
                mstore(mc, mload(cc))
            }

            // Add the length of _postBytes to the current length of tempBytes
            // and store it as the new length in the first 32 bytes of the
            // tempBytes memory.
            length := mload(_postBytes)
            mstore(tempBytes, add(length, mload(tempBytes)))

            // Move the memory counter back from a multiple of 0x20 to the
            // actual end of the _preBytes data.
            mc := end
            // Stop copying when the memory counter reaches the new combined
            // length of the arrays.
            end := add(mc, length)

            for {
                let cc := add(_postBytes, 0x20)
            } lt(mc, end) {
                mc := add(mc, 0x20)
                cc := add(cc, 0x20)
            } {
                mstore(mc, mload(cc))
            }

            // Update the free-memory pointer by padding our last write location
            // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
            // next 32 byte block, then round down to the nearest multiple of
            // 32. If the sum of the length of the two arrays is zero then add
            // one before rounding down to leave a blank 32 bytes (the length block with 0).
            mstore(0x40, and(
              add(add(end, iszero(add(length, mload(_preBytes)))), 31),
              not(31) // Round down to the nearest 32 bytes.
            ))
        }

        return tempBytes;
    }

    function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
        assembly {
            // Read the first 32 bytes of _preBytes storage, which is the length
            // of the array. (We don't need to use the offset into the slot
            // because arrays use the entire slot.)
            let fslot := sload(_preBytes.slot)
            // Arrays of 31 bytes or less have an even value in their slot,
            // while longer arrays have an odd value. The actual length is
            // the slot divided by two for odd values, and the lowest order
            // byte divided by two for even values.
            // If the slot is even, bitwise and the slot with 255 and divide by
            // two to get the length. If the slot is odd, bitwise and the slot
            // with -1 and divide by two.
            let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
            let mlength := mload(_postBytes)
            let newlength := add(slength, mlength)
            // slength can contain both the length and contents of the array
            // if length < 32 bytes so let's prepare for that
            // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
            switch add(lt(slength, 32), lt(newlength, 32))
            case 2 {
                // Since the new array still fits in the slot, we just need to
                // update the contents of the slot.
                // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
                sstore(
                    _preBytes.slot,
                    // all the modifications to the slot are inside this
                    // next block
                    add(
                        // we can just add to the slot contents because the
                        // bytes we want to change are the LSBs
                        fslot,
                        add(
                            mul(
                                div(
                                    // load the bytes from memory
                                    mload(add(_postBytes, 0x20)),
                                    // zero all bytes to the right
                                    exp(0x100, sub(32, mlength))
                                ),
                                // and now shift left the number of bytes to
                                // leave space for the length in the slot
                                exp(0x100, sub(32, newlength))
                            ),
                            // increase length by the double of the memory
                            // bytes length
                            mul(mlength, 2)
                        )
                    )
                )
            }
            case 1 {
                // The stored value fits in the slot, but the combined value
                // will exceed it.
                // get the keccak hash to get the contents of the array
                mstore(0x0, _preBytes.slot)
                let sc := add(keccak256(0x0, 0x20), div(slength, 32))

                // save new length
                sstore(_preBytes.slot, add(mul(newlength, 2), 1))

                // The contents of the _postBytes array start 32 bytes into
                // the structure. Our first read should obtain the `submod`
                // bytes that can fit into the unused space in the last word
                // of the stored array. To get this, we read 32 bytes starting
                // from `submod`, so the data we read overlaps with the array
                // contents by `submod` bytes. Masking the lowest-order
                // `submod` bytes allows us to add that value directly to the
                // stored value.

                let submod := sub(32, slength)
                let mc := add(_postBytes, submod)
                let end := add(_postBytes, mlength)
                let mask := sub(exp(0x100, submod), 1)

                sstore(
                    sc,
                    add(
                        and(
                            fslot,
                            0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
                        ),
                        and(mload(mc), mask)
                    )
                )

                for {
                    mc := add(mc, 0x20)
                    sc := add(sc, 1)
                } lt(mc, end) {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } {
                    sstore(sc, mload(mc))
                }

                mask := exp(0x100, sub(mc, end))

                sstore(sc, mul(div(mload(mc), mask), mask))
            }
            default {
                // get the keccak hash to get the contents of the array
                mstore(0x0, _preBytes.slot)
                // Start copying to the last used word of the stored array.
                let sc := add(keccak256(0x0, 0x20), div(slength, 32))

                // save new length
                sstore(_preBytes.slot, add(mul(newlength, 2), 1))

                // Copy over the first `submod` bytes of the new data as in
                // case 1 above.
                let slengthmod := mod(slength, 32)
                let mlengthmod := mod(mlength, 32)
                let submod := sub(32, slengthmod)
                let mc := add(_postBytes, submod)
                let end := add(_postBytes, mlength)
                let mask := sub(exp(0x100, submod), 1)

                sstore(sc, add(sload(sc), and(mload(mc), mask)))

                for {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } lt(mc, end) {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } {
                    sstore(sc, mload(mc))
                }

                mask := exp(0x100, sub(mc, end))

                sstore(sc, mul(div(mload(mc), mask), mask))
            }
        }
    }

    function slice(
        bytes memory _bytes,
        uint256 _start,
        uint256 _length
    )
        internal
        pure
        returns (bytes memory)
    {
        require(_length + 31 >= _length, "slice_overflow");
        require(_bytes.length >= _start + _length, "slice_outOfBounds");

        bytes memory tempBytes;

        assembly {
            switch iszero(_length)
            case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                tempBytes := mload(0x40)

                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                let lengthmod := and(_length, 31)

                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                let end := add(mc, _length)

                for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                    let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    mstore(mc, mload(cc))
                }

                mstore(tempBytes, _length)

                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                mstore(0x40, and(add(mc, 31), not(31)))
            }
            //if we want a zero-length slice let's just return a zero-length array
            default {
                tempBytes := mload(0x40)
                //zero out the 32 bytes slice we are about to return
                //we need to do it because Solidity does not garbage collect
                mstore(tempBytes, 0)

                mstore(0x40, add(tempBytes, 0x20))
            }
        }

        return tempBytes;
    }

    function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
        require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
        address tempAddress;

        assembly {
            tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
        }

        return tempAddress;
    }

    function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
        require(_bytes.length >= _start + 1 , "toUint8_outOfBounds");
        uint8 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x1), _start))
        }

        return tempUint;
    }

    function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
        require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
        uint16 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x2), _start))
        }

        return tempUint;
    }

    function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
        require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
        uint32 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x4), _start))
        }

        return tempUint;
    }

    function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
        require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
        uint64 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x8), _start))
        }

        return tempUint;
    }

    function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
        require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
        uint96 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0xc), _start))
        }

        return tempUint;
    }

    function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
        require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
        uint128 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x10), _start))
        }

        return tempUint;
    }

    function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
        require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
        uint256 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x20), _start))
        }

        return tempUint;
    }

    function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
        require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
        bytes32 tempBytes32;

        assembly {
            tempBytes32 := mload(add(add(_bytes, 0x20), _start))
        }

        return tempBytes32;
    }

    function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
        bool success = true;

        assembly {
            let length := mload(_preBytes)

            // if lengths don't match the arrays are not equal
            switch eq(length, mload(_postBytes))
            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(_preBytes, 0x20)
                let end := add(mc, length)

                for {
                    let cc := add(_postBytes, 0x20)
                // the next line is the loop condition:
                // while(uint256(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;
    }

    function equalStorage(
        bytes storage _preBytes,
        bytes memory _postBytes
    )
        internal
        view
        returns (bool)
    {
        bool success = true;

        assembly {
            // we know _preBytes_offset is 0
            let fslot := sload(_preBytes.slot)
            // Decode the length of the stored array like in concatStorage().
            let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
            let mlength := mload(_postBytes)

            // if lengths don't match the arrays are not equal
            switch eq(slength, mlength)
            case 1 {
                // slength can contain both the length and contents of the array
                // if length < 32 bytes so let's prepare for that
                // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                if iszero(iszero(slength)) {
                    switch lt(slength, 32)
                    case 1 {
                        // blank the last byte which is the length
                        fslot := mul(div(fslot, 0x100), 0x100)

                        if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
                            // unsuccess:
                            success := 0
                        }
                    }
                    default {
                        // 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

                        // get the keccak hash to get the contents of the array
                        mstore(0x0, _preBytes.slot)
                        let sc := keccak256(0x0, 0x20)

                        let mc := add(_postBytes, 0x20)
                        let end := add(mc, mlength)

                        // the next line is the loop condition:
                        // while(uint256(mc < end) + cb == 2)
                        for {} eq(add(lt(mc, end), cb), 2) {
                            sc := add(sc, 1)
                            mc := add(mc, 0x20)
                        } {
                            if iszero(eq(sload(sc), mload(mc))) {
                                // unsuccess:
                                success := 0
                                cb := 0
                            }
                        }
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }


    function toHex(bytes memory buffer) public pure returns (string memory) {

        // Fixed buffer size for hexadecimal convertion
        bytes memory converted = new bytes(buffer.length * 2);

        bytes memory _base = "0123456789abcdef";

        for (uint256 i = 0; i < buffer.length; i++) {
            converted[i * 2] = _base[uint8(buffer[i]) / _base.length];
            converted[i * 2 + 1] = _base[uint8(buffer[i]) % _base.length];
        }

        return string(abi.encodePacked("0x", converted));
    }
}

File 5 of 18 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}

File 6 of 18 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 7 of 18 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 8 of 18 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

File 9 of 18 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

File 10 of 18 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "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");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, 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) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // 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
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

File 11 of 18 : IERC721Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

File 12 of 18 : IERC721Enumerable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Enumerable is IERC721 {
    /**
     * @dev Returns the total amount of tokens stored by the contract.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
     * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);

    /**
     * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
     * Use along with {totalSupply} to enumerate all tokens.
     */
    function tokenByIndex(uint256 index) external view returns (uint256);
}

File 13 of 18 : ERC721Enumerable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Enumerable.sol)

pragma solidity ^0.8.0;

import "../ERC721.sol";
import "./IERC721Enumerable.sol";

/**
 * @dev This implements an optional extension of {ERC721} defined in the EIP that adds
 * enumerability of all the token ids in the contract as well as all token ids owned by each
 * account.
 */
abstract contract ERC721Enumerable is ERC721, IERC721Enumerable {
    // Mapping from owner to list of owned token IDs
    mapping(address => mapping(uint256 => uint256)) private _ownedTokens;

    // Mapping from token ID to index of the owner tokens list
    mapping(uint256 => uint256) private _ownedTokensIndex;

    // Array with all token ids, used for enumeration
    uint256[] private _allTokens;

    // Mapping from token id to position in the allTokens array
    mapping(uint256 => uint256) private _allTokensIndex;

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) {
        return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
        require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
        return _ownedTokens[owner][index];
    }

    /**
     * @dev See {IERC721Enumerable-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _allTokens.length;
    }

    /**
     * @dev See {IERC721Enumerable-tokenByIndex}.
     */
    function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
        require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds");
        return _allTokens[index];
    }

    /**
     * @dev See {ERC721-_beforeTokenTransfer}.
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 firstTokenId,
        uint256 batchSize
    ) internal virtual override {
        super._beforeTokenTransfer(from, to, firstTokenId, batchSize);

        if (batchSize > 1) {
            // Will only trigger during construction. Batch transferring (minting) is not available afterwards.
            revert("ERC721Enumerable: consecutive transfers not supported");
        }

        uint256 tokenId = firstTokenId;

        if (from == address(0)) {
            _addTokenToAllTokensEnumeration(tokenId);
        } else if (from != to) {
            _removeTokenFromOwnerEnumeration(from, tokenId);
        }
        if (to == address(0)) {
            _removeTokenFromAllTokensEnumeration(tokenId);
        } else if (to != from) {
            _addTokenToOwnerEnumeration(to, tokenId);
        }
    }

    /**
     * @dev Private function to add a token to this extension's ownership-tracking data structures.
     * @param to address representing the new owner of the given token ID
     * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
     */
    function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
        uint256 length = ERC721.balanceOf(to);
        _ownedTokens[to][length] = tokenId;
        _ownedTokensIndex[tokenId] = length;
    }

    /**
     * @dev Private function to add a token to this extension's token tracking data structures.
     * @param tokenId uint256 ID of the token to be added to the tokens list
     */
    function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
        _allTokensIndex[tokenId] = _allTokens.length;
        _allTokens.push(tokenId);
    }

    /**
     * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
     * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
     * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
     * This has O(1) time complexity, but alters the order of the _ownedTokens array.
     * @param from address representing the previous owner of the given token ID
     * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
     */
    function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
        // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = ERC721.balanceOf(from) - 1;
        uint256 tokenIndex = _ownedTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary
        if (tokenIndex != lastTokenIndex) {
            uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];

            _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
            _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
        }

        // This also deletes the contents at the last position of the array
        delete _ownedTokensIndex[tokenId];
        delete _ownedTokens[from][lastTokenIndex];
    }

    /**
     * @dev Private function to remove a token from this extension's token tracking data structures.
     * This has O(1) time complexity, but alters the order of the _allTokens array.
     * @param tokenId uint256 ID of the token to be removed from the tokens list
     */
    function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
        // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = _allTokens.length - 1;
        uint256 tokenIndex = _allTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
        // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
        // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
        uint256 lastTokenId = _allTokens[lastTokenIndex];

        _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
        _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index

        // This also deletes the contents at the last position of the array
        delete _allTokensIndex[tokenId];
        _allTokens.pop();
    }
}

File 14 of 18 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

File 15 of 18 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

File 16 of 18 : ERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.0;

import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "./extensions/IERC721Metadata.sol";
import "../../utils/Address.sol";
import "../../utils/Context.sol";
import "../../utils/Strings.sol";
import "../../utils/introspection/ERC165.sol";

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
    using Address for address;
    using Strings for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to owner address
    mapping(uint256 => address) private _owners;

    // Mapping owner address to token count
    mapping(address => uint256) private _balances;

    // Mapping from token ID to approved address
    mapping(uint256 => address) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual override returns (uint256) {
        require(owner != address(0), "ERC721: address zero is not a valid owner");
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _ownerOf(tokenId);
        require(owner != address(0), "ERC721: invalid token ID");
        return owner;
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        _requireMinted(tokenId);

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overridden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual override {
        address owner = ERC721.ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(
            _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
            "ERC721: approve caller is not token owner or approved for all"
        );

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        _requireMinted(tokenId);

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");

        _transfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) public virtual override {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
        _safeTransfer(from, to, tokenId, data);
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * `data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
     */
    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
        return _owners[tokenId];
    }

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted (`_mint`),
     * and stop existing when they are burned (`_burn`).
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _ownerOf(tokenId) != address(0);
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
        address owner = ERC721.ownerOf(tokenId);
        return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
    }

    /**
     * @dev Safely mints `tokenId` and transfers it to `to`.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        _mint(to, tokenId);
        require(
            _checkOnERC721Received(address(0), to, tokenId, data),
            "ERC721: transfer to non ERC721Receiver implementer"
        );
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");

        _beforeTokenTransfer(address(0), to, tokenId, 1);

        // Check that tokenId was not minted by `_beforeTokenTransfer` hook
        require(!_exists(tokenId), "ERC721: token already minted");

        unchecked {
            // Will not overflow unless all 2**256 token ids are minted to the same owner.
            // Given that tokens are minted one by one, it is impossible in practice that
            // this ever happens. Might change if we allow batch minting.
            // The ERC fails to describe this case.
            _balances[to] += 1;
        }

        _owners[tokenId] = to;

        emit Transfer(address(0), to, tokenId);

        _afterTokenTransfer(address(0), to, tokenId, 1);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     * This is an internal function that does not check if the sender is authorized to operate on the token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal virtual {
        address owner = ERC721.ownerOf(tokenId);

        _beforeTokenTransfer(owner, address(0), tokenId, 1);

        // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook
        owner = ERC721.ownerOf(tokenId);

        // Clear approvals
        delete _tokenApprovals[tokenId];

        unchecked {
            // Cannot overflow, as that would require more tokens to be burned/transferred
            // out than the owner initially received through minting and transferring in.
            _balances[owner] -= 1;
        }
        delete _owners[tokenId];

        emit Transfer(owner, address(0), tokenId);

        _afterTokenTransfer(owner, address(0), tokenId, 1);
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
        require(to != address(0), "ERC721: transfer to the zero address");

        _beforeTokenTransfer(from, to, tokenId, 1);

        // Check that tokenId was not transferred by `_beforeTokenTransfer` hook
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");

        // Clear approvals from the previous owner
        delete _tokenApprovals[tokenId];

        unchecked {
            // `_balances[from]` cannot overflow for the same reason as described in `_burn`:
            // `from`'s balance is the number of token held, which is at least one before the current
            // transfer.
            // `_balances[to]` could overflow in the conditions described in `_mint`. That would require
            // all 2**256 token ids to be minted, which in practice is impossible.
            _balances[from] -= 1;
            _balances[to] += 1;
        }
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        _afterTokenTransfer(from, to, tokenId, 1);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * Emits an {Approval} event.
     */
    function _approve(address to, uint256 tokenId) internal virtual {
        _tokenApprovals[tokenId] = to;
        emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits an {ApprovalForAll} event.
     */
    function _setApprovalForAll(
        address owner,
        address operator,
        bool approved
    ) internal virtual {
        require(owner != operator, "ERC721: approve to caller");
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Reverts if the `tokenId` has not been minted yet.
     */
    function _requireMinted(uint256 tokenId) internal view virtual {
        require(_exists(tokenId), "ERC721: invalid token ID");
    }

    /**
     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
     * The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param data bytes optional data to send along with the call
     * @return bool whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) private returns (bool) {
        if (to.isContract()) {
            try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is
     * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`.
     * - When `from` is zero, the tokens will be minted for `to`.
     * - When `to` is zero, ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     * - `batchSize` is non-zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256, /* firstTokenId */
        uint256 batchSize
    ) internal virtual {
        if (batchSize > 1) {
            if (from != address(0)) {
                _balances[from] -= batchSize;
            }
            if (to != address(0)) {
                _balances[to] += batchSize;
            }
        }
    }

    /**
     * @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is
     * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`.
     * - When `from` is zero, the tokens were minted for `to`.
     * - When `to` is zero, ``from``'s tokens were burned.
     * - `from` and `to` are never both zero.
     * - `batchSize` is non-zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 firstTokenId,
        uint256 batchSize
    ) internal virtual {}
}

File 17 of 18 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);
}

File 18 of 18 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

Settings
{
  "remappings": [],
  "optimizer": {
    "enabled": false,
    "runs": 100
  },
  "evmVersion": "london",
  "libraries": {
    "/contracts/GIF89a.sol": {
      "GIF89a": "0x83B9d161Ad0f2a54663FA579a2B5dd4799bbCd46"
    }
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"manager_","type":"address"},{"internalType":"bytes3[]","name":"colors_","type":"bytes3[]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"_BondingCurveAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_EETRenderEngineAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_artist","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"_colors","outputs":[{"internalType":"bytes3","name":"","type":"bytes3"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_colorsFrozen","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_depsFrozen","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_guaContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_manager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_scoreBoardAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"},{"internalType":"address","name":"_currency","type":"address"},{"internalType":"bytes","name":"_burnPayload","type":"bytes"}],"name":"burnToCurve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_owners","type":"address[]"},{"internalType":"bytes32[]","name":"_queryhash","type":"bytes32[]"},{"internalType":"uint256[]","name":"_rand","type":"uint256[]"},{"internalType":"address","name":"_currency","type":"address"},{"internalType":"string[]","name":"_encrypteds","type":"string[]"},{"internalType":"bytes","name":"_mintPayload","type":"bytes"}],"name":"mint","outputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"guaContract_","type":"address"},{"internalType":"address","name":"BondingCurveAddress_","type":"address"},{"internalType":"address","name":"EETRenderEngineAddress_","type":"address"},{"internalType":"address","name":"ScoreBoardAddress_","type":"address"},{"internalType":"bool","name":"_freeze","type":"bool"}],"name":"setDependencies","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"manager_","type":"address"}],"name":"setManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenAPI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040526000600e60006101000a81548160ff0219169083151502179055506009600f55600f601055600060f81b601160146101000a81548160ff021916908360f81c0217905550600460f81b601160156101000a81548160ff021916908360f81c021790555061640060f01b601160166101000a81548161ffff021916908360f01c021790555060006014806101000a81548160ff021916908315150217905550348015620000af57600080fd5b506040516200688d3803806200688d8339818101604052810190620000d5919062000cb4565b6040518060400160405280600381526020017f45455400000000000000000000000000000000000000000000000000000000008152506040518060400160405280600381526020017f454554000000000000000000000000000000000000000000000000000000000081525081600090805190602001906200015992919062000876565b5080600190805190602001906200017292919062000876565b5050506001600a8190555033600b60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600c60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555062000210816200021860201b60201c565b5050620016a1565b600c60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480620002c25750600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b62000304576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620002fb9062000d7b565b60405180910390fd5b80600d90805190602001906200031c92919062000907565b5060007383b9d161ad0f2a54663fa579a2b5dd4799bbcd4663cb3020a2600d6040518263ffffffff1660e01b8152600401620003599190620012ee565b602060405180830381865af415801562000377573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200039d91906200136f565b905060006200054d620004b97383b9d161ad0f2a54663fa579a2b5dd4799bbcd4663cc51ba376040518163ffffffff1660e01b8152600401600060405180830381865af4158015620003f3573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906200041e919062001490565b7383b9d161ad0f2a54663fa579a2b5dd4799bbcd4663c421700f600f54601054886040518463ffffffff1660e01b81526004016200045f939291906200150d565b600060405180830381865af41580156200047d573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250810190620004a8919062001490565b620007e860201b62001a1e1760201c565b7383b9d161ad0f2a54663fa579a2b5dd4799bbcd466339348b86600d6040518263ffffffff1660e01b8152600401620004f39190620012ee565b600060405180830381865af415801562000511573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906200053c919062001490565b620007e860201b62001a1e1760201c565b905060007383b9d161ad0f2a54663fa579a2b5dd4799bbcd46637573346260016040518263ffffffff1660e01b81526004016200058b919062001569565b600060405180830381865af4158015620005a9573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250810190620005d4919062001490565b905060007383b9d161ad0f2a54663fa579a2b5dd4799bbcd4663c9318ac16001601160159054906101000a900460f81b601160169054906101000a900460f01b6001601160149054906101000a900460f81b6040518663ffffffff1660e01b815260040162000648959493929190620015e0565b600060405180830381865af415801562000666573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525081019062000691919062001490565b905060007383b9d161ad0f2a54663fa579a2b5dd4799bbcd46639125c45c6040518163ffffffff1660e01b8152600401600060405180830381865af4158015620006df573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906200070a919062001490565b905060006200073b856200072a8686620007e860201b62001a1e1760201c565b620007e860201b62001a1e1760201c565b9050604051806080016040528082815260200186815260200184815260200183815250601560008201518160000190805190602001906200077e929190620009b9565b5060208201518160010190805190602001906200079d929190620009b9565b506040820151816002019080519060200190620007bc929190620009b9565b506060820151816003019080519060200190620007db929190620009b9565b5090505050505050505050565b6060806040519050835180825260208201818101602087015b8183101562000820578051835260208301925060208101905062000801565b50855192508351830184528091508282019050602086015b8183101562000857578051835260208301925060208101905062000838565b50601f19601f8851850115830101166040525050508091505092915050565b82805462000884906200166c565b90600052602060002090601f016020900481019282620008a85760008555620008f4565b82601f10620008c357805160ff1916838001178555620008f4565b82800160010185558215620008f4579182015b82811115620008f3578251825591602001919060010190620008d6565b5b50905062000903919062000a4a565b5090565b82805482825590600052602060002090600901600a90048101928215620009a65791602002820160005b838211156200097357835183826101000a81548162ffffff021916908360e81c0217905550926020019260030160208160020104928301926001030262000931565b8015620009a45782816101000a81549062ffffff021916905560030160208160020104928301926001030262000973565b505b509050620009b5919062000a4a565b5090565b828054620009c7906200166c565b90600052602060002090601f016020900481019282620009eb576000855562000a37565b82601f1062000a0657805160ff191683800117855562000a37565b8280016001018555821562000a37579182015b8281111562000a3657825182559160200191906001019062000a19565b5b50905062000a46919062000a4a565b5090565b5b8082111562000a6557600081600090555060010162000a4b565b5090565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600062000aaa8262000a7d565b9050919050565b62000abc8162000a9d565b811462000ac857600080fd5b50565b60008151905062000adc8162000ab1565b92915050565b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b62000b328262000ae7565b810181811067ffffffffffffffff8211171562000b545762000b5362000af8565b5b80604052505050565b600062000b6962000a69565b905062000b77828262000b27565b919050565b600067ffffffffffffffff82111562000b9a5762000b9962000af8565b5b602082029050602081019050919050565b600080fd5b60007fffffff000000000000000000000000000000000000000000000000000000000082169050919050565b62000be78162000bb0565b811462000bf357600080fd5b50565b60008151905062000c078162000bdc565b92915050565b600062000c2462000c1e8462000b7c565b62000b5d565b9050808382526020820190506020840283018581111562000c4a5762000c4962000bab565b5b835b8181101562000c77578062000c62888262000bf6565b84526020840193505060208101905062000c4c565b5050509392505050565b600082601f83011262000c995762000c9862000ae2565b5b815162000cab84826020860162000c0d565b91505092915050565b6000806040838503121562000cce5762000ccd62000a73565b5b600062000cde8582860162000acb565b925050602083015167ffffffffffffffff81111562000d025762000d0162000a78565b5b62000d108582860162000c81565b9150509250929050565b600082825260208201905092915050565b7f6100000000000000000000000000000000000000000000000000000000000000600082015250565b600062000d6360018362000d1a565b915062000d708262000d2b565b602082019050919050565b6000602082019050818103600083015262000d968162000d54565b9050919050565b600081549050919050565b600082825260208201905092915050565b60008190508160005260206000209050919050565b62000dd98162000bb0565b82525050565b60008160001c9050919050565b60008160e81b9050919050565b600062000e068262000dec565b9050919050565b600062000e2462000e1e8362000ddf565b62000df9565b9050919050565b60008160181c9050919050565b600062000e4f62000e498362000e2b565b62000df9565b9050919050565b60008160301c9050919050565b600062000e7a62000e748362000e56565b62000df9565b9050919050565b60008160481c9050919050565b600062000ea562000e9f8362000e81565b62000df9565b9050919050565b60008160601c9050919050565b600062000ed062000eca8362000eac565b62000df9565b9050919050565b60008160781c9050919050565b600062000efb62000ef58362000ed7565b62000df9565b9050919050565b60008160901c9050919050565b600062000f2662000f208362000f02565b62000df9565b9050919050565b60008160a81c9050919050565b600062000f5162000f4b8362000f2d565b62000df9565b9050919050565b60008160c01c9050919050565b600062000f7c62000f768362000f58565b62000df9565b9050919050565b60008160d81c9050919050565b600062000fa762000fa18362000f83565b62000df9565b9050919050565b600062000fbb8262000d9d565b62000fc7818562000da8565b93508362000fd58462000db9565b60006001156200111d575b836001600a03820110156200111c5781546200100788620010018362000e0d565b62000dce565b60208801975062001023886200101d8362000e38565b62000dce565b6020880197506200103f88620010398362000e63565b62000dce565b6020880197506200105b88620010558362000e8e565b62000dce565b6020880197506200107788620010718362000eb9565b62000dce565b60208801975062001093886200108d8362000ee4565b62000dce565b602088019750620010af88620010a98362000f0f565b62000dce565b602088019750620010cb88620010c58362000f3a565b62000dce565b602088019750620010e788620010e18362000f65565b62000dce565b6020880197506200110388620010fd8362000f90565b62000dce565b60208801975060018301925050600a8101905062000fe0565b5b600115620012e15781548482101562001153576200114688620011408362000e0d565b62000dce565b6020880197506001820191505b848210156200117f5762001172886200116c8362000e38565b62000dce565b6020880197506001820191505b84821015620011ab576200119e88620011988362000e63565b62000dce565b6020880197506001820191505b84821015620011d757620011ca88620011c48362000e8e565b62000dce565b6020880197506001820191505b848210156200120357620011f688620011f08362000eb9565b62000dce565b6020880197506001820191505b848210156200122f5762001222886200121c8362000ee4565b62000dce565b6020880197506001820191505b848210156200125b576200124e88620012488362000f0f565b62000dce565b6020880197506001820191505b8482101562001287576200127a88620012748362000f3a565b62000dce565b6020880197506001820191505b84821015620012b357620012a688620012a08362000f65565b62000dce565b6020880197506001820191505b84821015620012df57620012d288620012cc8362000f90565b62000dce565b6020880197506001820191505b505b8694505050505092915050565b600060208201905081810360008301526200130a818462000fae565b905092915050565b60007fff0000000000000000000000000000000000000000000000000000000000000082169050919050565b620013498162001312565b81146200135557600080fd5b50565b60008151905062001369816200133e565b92915050565b60006020828403121562001388576200138762000a73565b5b6000620013988482850162001358565b91505092915050565b600080fd5b600067ffffffffffffffff821115620013c457620013c362000af8565b5b620013cf8262000ae7565b9050602081019050919050565b60005b83811015620013fc578082015181840152602081019050620013df565b838111156200140c576000848401525b50505050565b6000620014296200142384620013a6565b62000b5d565b905082815260208101848484011115620014485762001447620013a1565b5b62001455848285620013dc565b509392505050565b600082601f83011262001475576200147462000ae2565b5b81516200148784826020860162001412565b91505092915050565b600060208284031215620014a957620014a862000a73565b5b600082015167ffffffffffffffff811115620014ca57620014c962000a78565b5b620014d8848285016200145d565b91505092915050565b6000819050919050565b620014f681620014e1565b82525050565b620015078162001312565b82525050565b6000606082019050620015246000830186620014eb565b620015336020830185620014eb565b620015426040830184620014fc565b949350505050565b600061ffff82169050919050565b62001563816200154a565b82525050565b600060208201905062001580600083018462001558565b92915050565b60008115159050919050565b6200159d8162001586565b82525050565b60007fffff00000000000000000000000000000000000000000000000000000000000082169050919050565b620015da81620015a3565b82525050565b600060a082019050620015f7600083018862001592565b620016066020830187620014fc565b620016156040830186620015cf565b62001624606083018562001592565b620016336080830184620014fc565b9695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200168557607f821691505b6020821081036200169b576200169a6200163d565b5b50919050565b6151dc80620016b16000396000f3fe60806040526004361061018d5760003560e01c806378a7ee6b116100d7578063b88d4fde11610085578063b88d4fde146105cf578063c6903b4d146105f8578063c87b56dd14610623578063d0ebdbe714610660578063e0347e1c14610689578063e8801de0146106b4578063e985e9c5146106dd578063f0329f6b1461071a5761018d565b806378a7ee6b1461048f5780637e114c0e146104cc578063899e42f9146104fc57806395d89b4114610527578063a22cb46514610552578063a2ac63351461057b578063aede32f4146105a65761018d565b80632f745c591161013f5780632f745c59146102df57806335c04b841461031c57806336e69514146103595780634046f91a1461038457806342842e0e146103af5780634f6ccce7146103d85780636352211e1461041557806370a08231146104525761018d565b806301ffc9a71461019257806306fdde03146101cf578063081812fc146101fa578063095ea7b31461023757806318160ddd1461026057806323b872dd1461028b57806325ba071b146102b4575b600080fd5b34801561019e57600080fd5b506101b960048036038101906101b49190612f9f565b610745565b6040516101c69190612fe7565b60405180910390f35b3480156101db57600080fd5b506101e46107bf565b6040516101f1919061309b565b60405180910390f35b34801561020657600080fd5b50610221600480360381019061021c91906130f3565b610851565b60405161022e9190613161565b60405180910390f35b34801561024357600080fd5b5061025e600480360381019061025991906131a8565b610897565b005b34801561026c57600080fd5b506102756109ae565b60405161028291906131f7565b60405180910390f35b34801561029757600080fd5b506102b260048036038101906102ad9190613212565b6109bb565b005b3480156102c057600080fd5b506102c9610a1b565b6040516102d69190612fe7565b60405180910390f35b3480156102eb57600080fd5b50610306600480360381019061030191906131a8565b610a2e565b60405161031391906131f7565b60405180910390f35b34801561032857600080fd5b50610343600480360381019061033e91906130f3565b610ad3565b604051610350919061309b565b60405180910390f35b34801561036557600080fd5b5061036e610bce565b60405161037b9190612fe7565b60405180910390f35b34801561039057600080fd5b50610399610bdf565b6040516103a69190613161565b60405180910390f35b3480156103bb57600080fd5b506103d660048036038101906103d19190613212565b610c05565b005b3480156103e457600080fd5b506103ff60048036038101906103fa91906130f3565b610c25565b60405161040c91906131f7565b60405180910390f35b34801561042157600080fd5b5061043c600480360381019061043791906130f3565b610c96565b6040516104499190613161565b60405180910390f35b34801561045e57600080fd5b5061047960048036038101906104749190613265565b610d1c565b60405161048691906131f7565b60405180910390f35b34801561049b57600080fd5b506104b660048036038101906104b191906130f3565b610dd3565b6040516104c391906132cd565b60405180910390f35b6104e660048036038101906104e19190613823565b610e0a565b6040516104f391906139fa565b60405180910390f35b34801561050857600080fd5b5061051161123c565b60405161051e9190613161565b60405180910390f35b34801561053357600080fd5b5061053c611262565b604051610549919061309b565b60405180910390f35b34801561055e57600080fd5b5061057960048036038101906105749190613a48565b6112f4565b005b34801561058757600080fd5b5061059061130a565b60405161059d9190613161565b60405180910390f35b3480156105b257600080fd5b506105cd60048036038101906105c89190613a88565b611330565b005b3480156105db57600080fd5b506105f660048036038101906105f19190613b13565b6114b1565b005b34801561060457600080fd5b5061060d611513565b60405161061a9190613161565b60405180910390f35b34801561062f57600080fd5b5061064a600480360381019061064591906130f3565b611539565b604051610657919061309b565b60405180910390f35b34801561066c57600080fd5b5061068760048036038101906106829190613265565b611610565b005b34801561069557600080fd5b5061069e6116e4565b6040516106ab9190613161565b60405180910390f35b3480156106c057600080fd5b506106db60048036038101906106d69190613b96565b61170a565b005b3480156106e957600080fd5b5061070460048036038101906106ff9190613c11565b611964565b6040516107119190612fe7565b60405180910390f35b34801561072657600080fd5b5061072f6119f8565b60405161073c9190613161565b60405180910390f35b60007f780e9d63000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806107b857506107b782611aa8565b5b9050919050565b6060600080546107ce90613c80565b80601f01602080910402602001604051908101604052809291908181526020018280546107fa90613c80565b80156108475780601f1061081c57610100808354040283529160200191610847565b820191906000526020600020905b81548152906001019060200180831161082a57829003601f168201915b5050505050905090565b600061085c82611b8a565b6004600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b60006108a282610c96565b90508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610912576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161090990613d23565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff16610931611bd5565b73ffffffffffffffffffffffffffffffffffffffff161480610960575061095f8161095a611bd5565b611964565b5b61099f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161099690613db5565b60405180910390fd5b6109a98383611bdd565b505050565b6000600880549050905090565b6109cc6109c6611bd5565b82611c96565b610a0b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a0290613e47565b60405180910390fd5b610a16838383611d2b565b505050565b600e60009054906101000a900460ff1681565b6000610a3983610d1c565b8210610a7a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a7190613ed9565b60405180910390fd5b600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002054905092915050565b6060601360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633fb1f67d83601160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600d6015600001601460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518663ffffffff1660e01b8152600401610b81959493929190614460565b600060405180830381865afa158015610b9e573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250810190610bc79190614531565b9050919050565b60148054906101000a900460ff1681565b600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610c20838383604051806020016040528060008152506114b1565b505050565b6000610c2f6109ae565b8210610c70576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c67906145ec565b60405180910390fd5b60088281548110610c8457610c8361460c565b5b90600052602060002001549050919050565b600080610ca283612024565b9050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610d13576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d0a90614687565b60405180910390fd5b80915050919050565b60008073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610d8c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d8390614719565b60405180910390fd5b600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600d8181548110610de357600080fd5b90600052602060002090600a9182820401919006600302915054906101000a900460e81b81565b6060610e14612061565b6000601260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dfcf048d8851876040518363ffffffff1660e01b8152600401610e74929190614739565b602060405180830381865afa158015610e91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eb59190614777565b9050601260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d952411f345a9033858c518b8a6040518863ffffffff1660e01b8152600401610f1e9594939291906147e8565b602060405180830381858988f1158015610f3c573d6000803e3d6000fd5b5050505050506040513d601f19601f82011682018060405250810190610f629190614857565b610fa1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f98906148d0565b60405180910390fd5b865167ffffffffffffffff811115610fbc57610fbb6132ed565b5b604051908082528060200260200182016040528015610fea5781602001602082028036833780820191505090505b50915060005b875181101561122857601160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663035868b4601260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168a848151811061106d5761106c61460c565b5b60200260200101518a85815181106110885761108761460c565b5b60200260200101518986815181106110a3576110a261460c565b5b60200260200101516040518563ffffffff1660e01b81526004016110ca94939291906148ff565b60408051808303816000875af11580156110e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061110c9190614960565b508382815181106111205761111f61460c565b5b602002602001018181525050601460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d7b1595c84838151811061117d5761117c61460c565b5b602002602001015133876040518463ffffffff1660e01b81526004016111a5939291906149a0565b600060405180830381600087803b1580156111bf57600080fd5b505af11580156111d3573d6000803e3d6000fd5b505050506112158982815181106111ed576111ec61460c565b5b60200260200101518483815181106112085761120761460c565b5b60200260200101516120b0565b808061122090614a0d565b915050610ff0565b50506112326120ce565b9695505050505050565b601360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60606001805461127190613c80565b80601f016020809104026020016040519081016040528092919081815260200182805461129d90613c80565b80156112ea5780601f106112bf576101008083540402835291602001916112ea565b820191906000526020600020905b8154815290600101906020018083116112cd57829003601f168201915b5050505050905090565b6113066112ff611bd5565b83836120d8565b5050565b601260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b611338612061565b60005b83518110156114a357600061136985838151811061135c5761135b61460c565b5b6020026020010151610c96565b90506113908583815181106113815761138061460c565b5b60200260200101513385612244565b601260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166340321e568684815181106113e1576113e061460c565b5b6020026020010151833388886040518663ffffffff1660e01b815260040161140d959493929190614a76565b6020604051808303816000875af115801561142c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114509190614857565b61148f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161148690614b1c565b60405180910390fd5b50808061149b90614a0d565b91505061133b565b506114ac6120ce565b505050565b6114c26114bc611bd5565b83611c96565b611501576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114f890613e47565b60405180910390fd5b61150d8484848461232c565b50505050565b601460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6060601360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c4e9d84e83601160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600d60156000016040518563ffffffff1660e01b81526004016115c39493929190614b3c565b600060405180830381865afa1580156115e0573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906116099190614531565b9050919050565b600c60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146116a0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161169790614bdb565b60405180910390fd5b80600c60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b601160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600c60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806117b35750600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b6117f2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117e990614c47565b60405180910390fd5b60148054906101000a900460ff1615611840576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161183790614b1c565b60405180910390fd5b84601160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555083601260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082601360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081601460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550806014806101000a81548160ff0219169083151502179055505050505050565b6000600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b600c60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6060806040519050835180825260208201818101602087015b81831015611a545780518352602083019250602081019050611a37565b50855192508351830184528091508282019050602086015b81831015611a895780518352602083019250602081019050611a6c565b50601f19601f8851850115830101166040525050508091505092915050565b60007f80ac58cd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480611b7357507f5b5e139f000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b80611b835750611b8282612388565b5b9050919050565b611b93816123f2565b611bd2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611bc990614687565b60405180910390fd5b50565b600033905090565b816004600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff16611c5083610c96565b73ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b600080611ca283610c96565b90508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161480611ce45750611ce38185611964565b5b80611d2257508373ffffffffffffffffffffffffffffffffffffffff16611d0a84610851565b73ffffffffffffffffffffffffffffffffffffffff16145b91505092915050565b8273ffffffffffffffffffffffffffffffffffffffff16611d4b82610c96565b73ffffffffffffffffffffffffffffffffffffffff1614611da1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611d9890614cd9565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603611e10576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611e0790614d6b565b60405180910390fd5b611e1d8383836001612433565b8273ffffffffffffffffffffffffffffffffffffffff16611e3d82610c96565b73ffffffffffffffffffffffffffffffffffffffff1614611e93576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611e8a90614cd9565b60405180910390fd5b6004600082815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055506001600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816002600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a461201f8383836001612591565b505050565b60006002600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b6002600a54036120a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161209d90614dd7565b60405180910390fd5b6002600a81905550565b6120ca828260405180602001604052806000815250612597565b5050565b6001600a81905550565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603612146576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161213d90614e43565b60405180910390fd5b80600560008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31836040516122379190612fe7565b60405180910390a3505050565b61224e8284611c96565b61228d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161228490614c47565b60405180910390fd5b612296836125f2565b601460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d1503d148433846040518463ffffffff1660e01b81526004016122f5939291906149a0565b600060405180830381600087803b15801561230f57600080fd5b505af1158015612323573d6000803e3d6000fd5b50505050505050565b612337848484611d2b565b61234384848484612740565b612382576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161237990614ed5565b60405180910390fd5b50505050565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b60008073ffffffffffffffffffffffffffffffffffffffff1661241483612024565b73ffffffffffffffffffffffffffffffffffffffff1614159050919050565b61243f848484846128c7565b6001811115612483576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161247a90614f67565b60405180910390fd5b6000829050600073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16036124ca576124c5816129ed565b612509565b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614612508576125078582612a36565b5b5b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff160361254b5761254681612ba3565b61258a565b8473ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614612589576125888482612c74565b5b5b5050505050565b50505050565b6125a18383612cf3565b6125ae6000848484612740565b6125ed576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016125e490614ed5565b60405180910390fd5b505050565b60006125fd82610c96565b905061260d816000846001612433565b61261682610c96565b90506004600083815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055506002600083815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905581600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a461273c816000846001612591565b5050565b60006127618473ffffffffffffffffffffffffffffffffffffffff16612f10565b156128ba578373ffffffffffffffffffffffffffffffffffffffff1663150b7a0261278a611bd5565b8786866040518563ffffffff1660e01b81526004016127ac9493929190614f87565b6020604051808303816000875af19250505080156127e857506040513d601f19601f820116820180604052508101906127e59190614fe8565b60015b61286a573d8060008114612818576040519150601f19603f3d011682016040523d82523d6000602084013e61281d565b606091505b506000815103612862576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161285990614ed5565b60405180910390fd5b805181602001fd5b63150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149150506128bf565b600190505b949350505050565b60018111156129e757600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161461295b5780600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546129539190615015565b925050819055505b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16146129e65780600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546129de9190615049565b925050819055505b5b50505050565b6008805490506009600083815260200190815260200160002081905550600881908060018154018082558091505060019003906000526020600020016000909190919091505550565b60006001612a4384610d1c565b612a4d9190615015565b9050600060076000848152602001908152602001600020549050818114612b32576000600660008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002054905080600660008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002081905550816007600083815260200190815260200160002081905550505b6007600084815260200190815260200160002060009055600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008381526020019081526020016000206000905550505050565b60006001600880549050612bb79190615015565b9050600060096000848152602001908152602001600020549050600060088381548110612be757612be661460c565b5b906000526020600020015490508060088381548110612c0957612c0861460c565b5b906000526020600020018190555081600960008381526020019081526020016000208190555060096000858152602001908152602001600020600090556008805480612c5857612c5761509f565b5b6001900381819060005260206000200160009055905550505050565b6000612c7f83610d1c565b905081600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002081905550806007600084815260200190815260200160002081905550505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603612d62576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612d599061511a565b60405180910390fd5b612d6b816123f2565b15612dab576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612da290615186565b60405180910390fd5b612db9600083836001612433565b612dc2816123f2565b15612e02576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612df990615186565b60405180910390fd5b6001600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816002600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4612f0c600083836001612591565b5050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000604051905090565b600080fd5b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b612f7c81612f47565b8114612f8757600080fd5b50565b600081359050612f9981612f73565b92915050565b600060208284031215612fb557612fb4612f3d565b5b6000612fc384828501612f8a565b91505092915050565b60008115159050919050565b612fe181612fcc565b82525050565b6000602082019050612ffc6000830184612fd8565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561303c578082015181840152602081019050613021565b8381111561304b576000848401525b50505050565b6000601f19601f8301169050919050565b600061306d82613002565b613077818561300d565b935061308781856020860161301e565b61309081613051565b840191505092915050565b600060208201905081810360008301526130b58184613062565b905092915050565b6000819050919050565b6130d0816130bd565b81146130db57600080fd5b50565b6000813590506130ed816130c7565b92915050565b60006020828403121561310957613108612f3d565b5b6000613117848285016130de565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061314b82613120565b9050919050565b61315b81613140565b82525050565b60006020820190506131766000830184613152565b92915050565b61318581613140565b811461319057600080fd5b50565b6000813590506131a28161317c565b92915050565b600080604083850312156131bf576131be612f3d565b5b60006131cd85828601613193565b92505060206131de858286016130de565b9150509250929050565b6131f1816130bd565b82525050565b600060208201905061320c60008301846131e8565b92915050565b60008060006060848603121561322b5761322a612f3d565b5b600061323986828701613193565b935050602061324a86828701613193565b925050604061325b868287016130de565b9150509250925092565b60006020828403121561327b5761327a612f3d565b5b600061328984828501613193565b91505092915050565b60007fffffff000000000000000000000000000000000000000000000000000000000082169050919050565b6132c781613292565b82525050565b60006020820190506132e260008301846132be565b92915050565b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61332582613051565b810181811067ffffffffffffffff82111715613344576133436132ed565b5b80604052505050565b6000613357612f33565b9050613363828261331c565b919050565b600067ffffffffffffffff821115613383576133826132ed565b5b602082029050602081019050919050565b600080fd5b60006133ac6133a784613368565b61334d565b905080838252602082019050602084028301858111156133cf576133ce613394565b5b835b818110156133f857806133e48882613193565b8452602084019350506020810190506133d1565b5050509392505050565b600082601f830112613417576134166132e8565b5b8135613427848260208601613399565b91505092915050565b600067ffffffffffffffff82111561344b5761344a6132ed565b5b602082029050602081019050919050565b6000819050919050565b61346f8161345c565b811461347a57600080fd5b50565b60008135905061348c81613466565b92915050565b60006134a56134a084613430565b61334d565b905080838252602082019050602084028301858111156134c8576134c7613394565b5b835b818110156134f157806134dd888261347d565b8452602084019350506020810190506134ca565b5050509392505050565b600082601f8301126135105761350f6132e8565b5b8135613520848260208601613492565b91505092915050565b600067ffffffffffffffff821115613544576135436132ed565b5b602082029050602081019050919050565b600061356861356384613529565b61334d565b9050808382526020820190506020840283018581111561358b5761358a613394565b5b835b818110156135b457806135a088826130de565b84526020840193505060208101905061358d565b5050509392505050565b600082601f8301126135d3576135d26132e8565b5b81356135e3848260208601613555565b91505092915050565b600067ffffffffffffffff821115613607576136066132ed565b5b602082029050602081019050919050565b600080fd5b600067ffffffffffffffff821115613638576136376132ed565b5b61364182613051565b9050602081019050919050565b82818337600083830152505050565b600061367061366b8461361d565b61334d565b90508281526020810184848401111561368c5761368b613618565b5b61369784828561364e565b509392505050565b600082601f8301126136b4576136b36132e8565b5b81356136c484826020860161365d565b91505092915050565b60006136e06136db846135ec565b61334d565b9050808382526020820190506020840283018581111561370357613702613394565b5b835b8181101561374a57803567ffffffffffffffff811115613728576137276132e8565b5b808601613735898261369f565b85526020850194505050602081019050613705565b5050509392505050565b600082601f830112613769576137686132e8565b5b81356137798482602086016136cd565b91505092915050565b600067ffffffffffffffff82111561379d5761379c6132ed565b5b6137a682613051565b9050602081019050919050565b60006137c66137c184613782565b61334d565b9050828152602081018484840111156137e2576137e1613618565b5b6137ed84828561364e565b509392505050565b600082601f83011261380a576138096132e8565b5b813561381a8482602086016137b3565b91505092915050565b60008060008060008060c087890312156138405761383f612f3d565b5b600087013567ffffffffffffffff81111561385e5761385d612f42565b5b61386a89828a01613402565b965050602087013567ffffffffffffffff81111561388b5761388a612f42565b5b61389789828a016134fb565b955050604087013567ffffffffffffffff8111156138b8576138b7612f42565b5b6138c489828a016135be565b94505060606138d589828a01613193565b935050608087013567ffffffffffffffff8111156138f6576138f5612f42565b5b61390289828a01613754565b92505060a087013567ffffffffffffffff81111561392357613922612f42565b5b61392f89828a016137f5565b9150509295509295509295565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b613971816130bd565b82525050565b60006139838383613968565b60208301905092915050565b6000602082019050919050565b60006139a78261393c565b6139b18185613947565b93506139bc83613958565b8060005b838110156139ed5781516139d48882613977565b97506139df8361398f565b9250506001810190506139c0565b5085935050505092915050565b60006020820190508181036000830152613a14818461399c565b905092915050565b613a2581612fcc565b8114613a3057600080fd5b50565b600081359050613a4281613a1c565b92915050565b60008060408385031215613a5f57613a5e612f3d565b5b6000613a6d85828601613193565b9250506020613a7e85828601613a33565b9150509250929050565b600080600060608486031215613aa157613aa0612f3d565b5b600084013567ffffffffffffffff811115613abf57613abe612f42565b5b613acb868287016135be565b9350506020613adc86828701613193565b925050604084013567ffffffffffffffff811115613afd57613afc612f42565b5b613b09868287016137f5565b9150509250925092565b60008060008060808587031215613b2d57613b2c612f3d565b5b6000613b3b87828801613193565b9450506020613b4c87828801613193565b9350506040613b5d878288016130de565b925050606085013567ffffffffffffffff811115613b7e57613b7d612f42565b5b613b8a878288016137f5565b91505092959194509250565b600080600080600060a08688031215613bb257613bb1612f3d565b5b6000613bc088828901613193565b9550506020613bd188828901613193565b9450506040613be288828901613193565b9350506060613bf388828901613193565b9250506080613c0488828901613a33565b9150509295509295909350565b60008060408385031215613c2857613c27612f3d565b5b6000613c3685828601613193565b9250506020613c4785828601613193565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680613c9857607f821691505b602082108103613cab57613caa613c51565b5b50919050565b7f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560008201527f7200000000000000000000000000000000000000000000000000000000000000602082015250565b6000613d0d60218361300d565b9150613d1882613cb1565b604082019050919050565b60006020820190508181036000830152613d3c81613d00565b9050919050565b7f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60008201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c000000602082015250565b6000613d9f603d8361300d565b9150613daa82613d43565b604082019050919050565b60006020820190508181036000830152613dce81613d92565b9050919050565b7f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560008201527f72206f7220617070726f76656400000000000000000000000000000000000000602082015250565b6000613e31602d8361300d565b9150613e3c82613dd5565b604082019050919050565b60006020820190508181036000830152613e6081613e24565b9050919050565b7f455243373231456e756d657261626c653a206f776e657220696e646578206f7560008201527f74206f6620626f756e6473000000000000000000000000000000000000000000602082015250565b6000613ec3602b8361300d565b9150613ece82613e67565b604082019050919050565b60006020820190508181036000830152613ef281613eb6565b9050919050565b600081549050919050565b600082825260208201905092915050565b60008190508160005260206000209050919050565b613f3381613292565b82525050565b60008160001c9050919050565b60008160e81b9050919050565b6000613f5e82613f46565b9050919050565b6000613f78613f7383613f39565b613f53565b9050919050565b60008160181c9050919050565b6000613f9f613f9a83613f7f565b613f53565b9050919050565b60008160301c9050919050565b6000613fc6613fc183613fa6565b613f53565b9050919050565b60008160481c9050919050565b6000613fed613fe883613fcd565b613f53565b9050919050565b60008160601c9050919050565b600061401461400f83613ff4565b613f53565b9050919050565b60008160781c9050919050565b600061403b6140368361401b565b613f53565b9050919050565b60008160901c9050919050565b600061406261405d83614042565b613f53565b9050919050565b60008160a81c9050919050565b600061408961408483614069565b613f53565b9050919050565b60008160c01c9050919050565b60006140b06140ab83614090565b613f53565b9050919050565b60008160d81c9050919050565b60006140d76140d2836140b7565b613f53565b9050919050565b60006140e982613ef9565b6140f38185613f04565b9350836140ff84613f15565b600060011561421c575b836001600a038201101561421b57815461412b8861412683613f65565b613f2a565b6020880197506141438861413e83613f8c565b613f2a565b60208801975061415b8861415683613fb3565b613f2a565b6020880197506141738861416e83613fda565b613f2a565b60208801975061418b8861418683614001565b613f2a565b6020880197506141a38861419e83614028565b613f2a565b6020880197506141bb886141b68361404f565b613f2a565b6020880197506141d3886141ce83614076565b613f2a565b6020880197506141eb886141e68361409d565b613f2a565b602088019750614203886141fe836140c4565b613f2a565b60208801975060018301925050600a81019050614109565b5b6001156143ad5781548482101561424c5761423f8861423a83613f65565b613f2a565b6020880197506001820191505b84821015614273576142668861426183613f8c565b613f2a565b6020880197506001820191505b8482101561429a5761428d8861428883613fb3565b613f2a565b6020880197506001820191505b848210156142c1576142b4886142af83613fda565b613f2a565b6020880197506001820191505b848210156142e8576142db886142d683614001565b613f2a565b6020880197506001820191505b8482101561430f57614302886142fd83614028565b613f2a565b6020880197506001820191505b8482101561433657614329886143248361404f565b613f2a565b6020880197506001820191505b8482101561435d576143508861434b83614076565b613f2a565b6020880197506001820191505b8482101561438457614377886143728361409d565b613f2a565b6020880197506001820191505b848210156143ab5761439e88614399836140c4565b613f2a565b6020880197506001820191505b505b8694505050505092915050565b600082825260208201905092915050565b60008190508160005260206000209050919050565b600081546143ed81613c80565b6143f781866143ba565b94506001821660008114614412576001811461442457614457565b60ff1983168652602086019350614457565b61442d856143cb565b60005b8381101561444f57815481890152600182019150602081019050614430565b808801955050505b50505092915050565b600060a08201905061447560008301886131e8565b6144826020830187613152565b818103604083015261449481866140de565b905081810360608301526144a881856143e0565b90506144b76080830184613152565b9695505050505050565b60006144d46144cf8461361d565b61334d565b9050828152602081018484840111156144f0576144ef613618565b5b6144fb84828561301e565b509392505050565b600082601f830112614518576145176132e8565b5b81516145288482602086016144c1565b91505092915050565b60006020828403121561454757614546612f3d565b5b600082015167ffffffffffffffff81111561456557614564612f42565b5b61457184828501614503565b91505092915050565b7f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60008201527f7574206f6620626f756e64730000000000000000000000000000000000000000602082015250565b60006145d6602c8361300d565b91506145e18261457a565b604082019050919050565b60006020820190508181036000830152614605816145c9565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4552433732313a20696e76616c696420746f6b656e2049440000000000000000600082015250565b600061467160188361300d565b915061467c8261463b565b602082019050919050565b600060208201905081810360008301526146a081614664565b9050919050565b7f4552433732313a2061646472657373207a65726f206973206e6f74206120766160008201527f6c6964206f776e65720000000000000000000000000000000000000000000000602082015250565b600061470360298361300d565b915061470e826146a7565b604082019050919050565b60006020820190508181036000830152614732816146f6565b9050919050565b600060408201905061474e60008301856131e8565b61475b6020830184613152565b9392505050565b600081519050614771816130c7565b92915050565b60006020828403121561478d5761478c612f3d565b5b600061479b84828501614762565b91505092915050565b600081519050919050565b60006147ba826147a4565b6147c481856143ba565b93506147d481856020860161301e565b6147dd81613051565b840191505092915050565b600060a0820190506147fd6000830188613152565b61480a60208301876131e8565b61481760408301866131e8565b6148246060830185613152565b818103608083015261483681846147af565b90509695505050505050565b60008151905061485181613a1c565b92915050565b60006020828403121561486d5761486c612f3d565b5b600061487b84828501614842565b91505092915050565b7f7500000000000000000000000000000000000000000000000000000000000000600082015250565b60006148ba60018361300d565b91506148c582614884565b602082019050919050565b600060208201905081810360008301526148e9816148ad565b9050919050565b6148f98161345c565b82525050565b60006080820190506149146000830187613152565b61492160208301866148f0565b61492e60408301856131e8565b81810360608301526149408184613062565b905095945050505050565b60008151905061495a81613466565b92915050565b6000806040838503121561497757614976612f3d565b5b600061498585828601614762565b92505060206149968582860161494b565b9150509250929050565b60006060820190506149b560008301866131e8565b6149c26020830185613152565b81810360408301526149d481846147af565b9050949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000614a18826130bd565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614a4a57614a496149de565b5b600182019050919050565b6000614a6082613120565b9050919050565b614a7081614a55565b82525050565b600060a082019050614a8b60008301886131e8565b614a986020830187613152565b614aa56040830186614a67565b614ab26060830185613152565b8181036080830152614ac481846147af565b90509695505050505050565b7f6600000000000000000000000000000000000000000000000000000000000000600082015250565b6000614b0660018361300d565b9150614b1182614ad0565b602082019050919050565b60006020820190508181036000830152614b3581614af9565b9050919050565b6000608082019050614b5160008301876131e8565b614b5e6020830186613152565b8181036040830152614b7081856140de565b90508181036060830152614b8481846143e0565b905095945050505050565b7f6d00000000000000000000000000000000000000000000000000000000000000600082015250565b6000614bc560018361300d565b9150614bd082614b8f565b602082019050919050565b60006020820190508181036000830152614bf481614bb8565b9050919050565b7f6100000000000000000000000000000000000000000000000000000000000000600082015250565b6000614c3160018361300d565b9150614c3c82614bfb565b602082019050919050565b60006020820190508181036000830152614c6081614c24565b9050919050565b7f4552433732313a207472616e736665722066726f6d20696e636f72726563742060008201527f6f776e6572000000000000000000000000000000000000000000000000000000602082015250565b6000614cc360258361300d565b9150614cce82614c67565b604082019050919050565b60006020820190508181036000830152614cf281614cb6565b9050919050565b7f4552433732313a207472616e7366657220746f20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b6000614d5560248361300d565b9150614d6082614cf9565b604082019050919050565b60006020820190508181036000830152614d8481614d48565b9050919050565b7f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00600082015250565b6000614dc1601f8361300d565b9150614dcc82614d8b565b602082019050919050565b60006020820190508181036000830152614df081614db4565b9050919050565b7f4552433732313a20617070726f766520746f2063616c6c657200000000000000600082015250565b6000614e2d60198361300d565b9150614e3882614df7565b602082019050919050565b60006020820190508181036000830152614e5c81614e20565b9050919050565b7f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560008201527f63656976657220696d706c656d656e7465720000000000000000000000000000602082015250565b6000614ebf60328361300d565b9150614eca82614e63565b604082019050919050565b60006020820190508181036000830152614eee81614eb2565b9050919050565b7f455243373231456e756d657261626c653a20636f6e736563757469766520747260008201527f616e7366657273206e6f7420737570706f727465640000000000000000000000602082015250565b6000614f5160358361300d565b9150614f5c82614ef5565b604082019050919050565b60006020820190508181036000830152614f8081614f44565b9050919050565b6000608082019050614f9c6000830187613152565b614fa96020830186613152565b614fb660408301856131e8565b8181036060830152614fc881846147af565b905095945050505050565b600081519050614fe281612f73565b92915050565b600060208284031215614ffe57614ffd612f3d565b5b600061500c84828501614fd3565b91505092915050565b6000615020826130bd565b915061502b836130bd565b92508282101561503e5761503d6149de565b5b828203905092915050565b6000615054826130bd565b915061505f836130bd565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115615094576150936149de565b5b828201905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b7f4552433732313a206d696e7420746f20746865207a65726f2061646472657373600082015250565b600061510460208361300d565b915061510f826150ce565b602082019050919050565b60006020820190508181036000830152615133816150f7565b9050919050565b7f4552433732313a20746f6b656e20616c7265616479206d696e74656400000000600082015250565b6000615170601c8361300d565b915061517b8261513a565b602082019050919050565b6000602082019050818103600083015261519f81615163565b905091905056fea2646970667358221220e60a118b265bffa0bee1156a3c15658b87605c5886ba72a04c6b3be6591e04c964736f6c634300080e0033000000000000000000000000074a8fa105bf658bde876c47d65999ea9622d1ee00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000003ffffff000000000000000000000000000000000000000000000000000000000035a98e00000000000000000000000000000000000000000000000000000000005f5fbc0000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x60806040526004361061018d5760003560e01c806378a7ee6b116100d7578063b88d4fde11610085578063b88d4fde146105cf578063c6903b4d146105f8578063c87b56dd14610623578063d0ebdbe714610660578063e0347e1c14610689578063e8801de0146106b4578063e985e9c5146106dd578063f0329f6b1461071a5761018d565b806378a7ee6b1461048f5780637e114c0e146104cc578063899e42f9146104fc57806395d89b4114610527578063a22cb46514610552578063a2ac63351461057b578063aede32f4146105a65761018d565b80632f745c591161013f5780632f745c59146102df57806335c04b841461031c57806336e69514146103595780634046f91a1461038457806342842e0e146103af5780634f6ccce7146103d85780636352211e1461041557806370a08231146104525761018d565b806301ffc9a71461019257806306fdde03146101cf578063081812fc146101fa578063095ea7b31461023757806318160ddd1461026057806323b872dd1461028b57806325ba071b146102b4575b600080fd5b34801561019e57600080fd5b506101b960048036038101906101b49190612f9f565b610745565b6040516101c69190612fe7565b60405180910390f35b3480156101db57600080fd5b506101e46107bf565b6040516101f1919061309b565b60405180910390f35b34801561020657600080fd5b50610221600480360381019061021c91906130f3565b610851565b60405161022e9190613161565b60405180910390f35b34801561024357600080fd5b5061025e600480360381019061025991906131a8565b610897565b005b34801561026c57600080fd5b506102756109ae565b60405161028291906131f7565b60405180910390f35b34801561029757600080fd5b506102b260048036038101906102ad9190613212565b6109bb565b005b3480156102c057600080fd5b506102c9610a1b565b6040516102d69190612fe7565b60405180910390f35b3480156102eb57600080fd5b50610306600480360381019061030191906131a8565b610a2e565b60405161031391906131f7565b60405180910390f35b34801561032857600080fd5b50610343600480360381019061033e91906130f3565b610ad3565b604051610350919061309b565b60405180910390f35b34801561036557600080fd5b5061036e610bce565b60405161037b9190612fe7565b60405180910390f35b34801561039057600080fd5b50610399610bdf565b6040516103a69190613161565b60405180910390f35b3480156103bb57600080fd5b506103d660048036038101906103d19190613212565b610c05565b005b3480156103e457600080fd5b506103ff60048036038101906103fa91906130f3565b610c25565b60405161040c91906131f7565b60405180910390f35b34801561042157600080fd5b5061043c600480360381019061043791906130f3565b610c96565b6040516104499190613161565b60405180910390f35b34801561045e57600080fd5b5061047960048036038101906104749190613265565b610d1c565b60405161048691906131f7565b60405180910390f35b34801561049b57600080fd5b506104b660048036038101906104b191906130f3565b610dd3565b6040516104c391906132cd565b60405180910390f35b6104e660048036038101906104e19190613823565b610e0a565b6040516104f391906139fa565b60405180910390f35b34801561050857600080fd5b5061051161123c565b60405161051e9190613161565b60405180910390f35b34801561053357600080fd5b5061053c611262565b604051610549919061309b565b60405180910390f35b34801561055e57600080fd5b5061057960048036038101906105749190613a48565b6112f4565b005b34801561058757600080fd5b5061059061130a565b60405161059d9190613161565b60405180910390f35b3480156105b257600080fd5b506105cd60048036038101906105c89190613a88565b611330565b005b3480156105db57600080fd5b506105f660048036038101906105f19190613b13565b6114b1565b005b34801561060457600080fd5b5061060d611513565b60405161061a9190613161565b60405180910390f35b34801561062f57600080fd5b5061064a600480360381019061064591906130f3565b611539565b604051610657919061309b565b60405180910390f35b34801561066c57600080fd5b5061068760048036038101906106829190613265565b611610565b005b34801561069557600080fd5b5061069e6116e4565b6040516106ab9190613161565b60405180910390f35b3480156106c057600080fd5b506106db60048036038101906106d69190613b96565b61170a565b005b3480156106e957600080fd5b5061070460048036038101906106ff9190613c11565b611964565b6040516107119190612fe7565b60405180910390f35b34801561072657600080fd5b5061072f6119f8565b60405161073c9190613161565b60405180910390f35b60007f780e9d63000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806107b857506107b782611aa8565b5b9050919050565b6060600080546107ce90613c80565b80601f01602080910402602001604051908101604052809291908181526020018280546107fa90613c80565b80156108475780601f1061081c57610100808354040283529160200191610847565b820191906000526020600020905b81548152906001019060200180831161082a57829003601f168201915b5050505050905090565b600061085c82611b8a565b6004600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b60006108a282610c96565b90508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610912576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161090990613d23565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff16610931611bd5565b73ffffffffffffffffffffffffffffffffffffffff161480610960575061095f8161095a611bd5565b611964565b5b61099f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161099690613db5565b60405180910390fd5b6109a98383611bdd565b505050565b6000600880549050905090565b6109cc6109c6611bd5565b82611c96565b610a0b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a0290613e47565b60405180910390fd5b610a16838383611d2b565b505050565b600e60009054906101000a900460ff1681565b6000610a3983610d1c565b8210610a7a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a7190613ed9565b60405180910390fd5b600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002054905092915050565b6060601360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633fb1f67d83601160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600d6015600001601460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518663ffffffff1660e01b8152600401610b81959493929190614460565b600060405180830381865afa158015610b9e573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250810190610bc79190614531565b9050919050565b60148054906101000a900460ff1681565b600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610c20838383604051806020016040528060008152506114b1565b505050565b6000610c2f6109ae565b8210610c70576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c67906145ec565b60405180910390fd5b60088281548110610c8457610c8361460c565b5b90600052602060002001549050919050565b600080610ca283612024565b9050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610d13576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d0a90614687565b60405180910390fd5b80915050919050565b60008073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610d8c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d8390614719565b60405180910390fd5b600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600d8181548110610de357600080fd5b90600052602060002090600a9182820401919006600302915054906101000a900460e81b81565b6060610e14612061565b6000601260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dfcf048d8851876040518363ffffffff1660e01b8152600401610e74929190614739565b602060405180830381865afa158015610e91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eb59190614777565b9050601260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d952411f345a9033858c518b8a6040518863ffffffff1660e01b8152600401610f1e9594939291906147e8565b602060405180830381858988f1158015610f3c573d6000803e3d6000fd5b5050505050506040513d601f19601f82011682018060405250810190610f629190614857565b610fa1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f98906148d0565b60405180910390fd5b865167ffffffffffffffff811115610fbc57610fbb6132ed565b5b604051908082528060200260200182016040528015610fea5781602001602082028036833780820191505090505b50915060005b875181101561122857601160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663035868b4601260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168a848151811061106d5761106c61460c565b5b60200260200101518a85815181106110885761108761460c565b5b60200260200101518986815181106110a3576110a261460c565b5b60200260200101516040518563ffffffff1660e01b81526004016110ca94939291906148ff565b60408051808303816000875af11580156110e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061110c9190614960565b508382815181106111205761111f61460c565b5b602002602001018181525050601460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d7b1595c84838151811061117d5761117c61460c565b5b602002602001015133876040518463ffffffff1660e01b81526004016111a5939291906149a0565b600060405180830381600087803b1580156111bf57600080fd5b505af11580156111d3573d6000803e3d6000fd5b505050506112158982815181106111ed576111ec61460c565b5b60200260200101518483815181106112085761120761460c565b5b60200260200101516120b0565b808061122090614a0d565b915050610ff0565b50506112326120ce565b9695505050505050565b601360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60606001805461127190613c80565b80601f016020809104026020016040519081016040528092919081815260200182805461129d90613c80565b80156112ea5780601f106112bf576101008083540402835291602001916112ea565b820191906000526020600020905b8154815290600101906020018083116112cd57829003601f168201915b5050505050905090565b6113066112ff611bd5565b83836120d8565b5050565b601260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b611338612061565b60005b83518110156114a357600061136985838151811061135c5761135b61460c565b5b6020026020010151610c96565b90506113908583815181106113815761138061460c565b5b60200260200101513385612244565b601260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166340321e568684815181106113e1576113e061460c565b5b6020026020010151833388886040518663ffffffff1660e01b815260040161140d959493929190614a76565b6020604051808303816000875af115801561142c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114509190614857565b61148f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161148690614b1c565b60405180910390fd5b50808061149b90614a0d565b91505061133b565b506114ac6120ce565b505050565b6114c26114bc611bd5565b83611c96565b611501576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114f890613e47565b60405180910390fd5b61150d8484848461232c565b50505050565b601460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6060601360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c4e9d84e83601160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600d60156000016040518563ffffffff1660e01b81526004016115c39493929190614b3c565b600060405180830381865afa1580156115e0573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906116099190614531565b9050919050565b600c60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146116a0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161169790614bdb565b60405180910390fd5b80600c60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b601160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600c60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806117b35750600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b6117f2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117e990614c47565b60405180910390fd5b60148054906101000a900460ff1615611840576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161183790614b1c565b60405180910390fd5b84601160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555083601260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082601360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081601460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550806014806101000a81548160ff0219169083151502179055505050505050565b6000600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b600c60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6060806040519050835180825260208201818101602087015b81831015611a545780518352602083019250602081019050611a37565b50855192508351830184528091508282019050602086015b81831015611a895780518352602083019250602081019050611a6c565b50601f19601f8851850115830101166040525050508091505092915050565b60007f80ac58cd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480611b7357507f5b5e139f000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b80611b835750611b8282612388565b5b9050919050565b611b93816123f2565b611bd2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611bc990614687565b60405180910390fd5b50565b600033905090565b816004600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff16611c5083610c96565b73ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b600080611ca283610c96565b90508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161480611ce45750611ce38185611964565b5b80611d2257508373ffffffffffffffffffffffffffffffffffffffff16611d0a84610851565b73ffffffffffffffffffffffffffffffffffffffff16145b91505092915050565b8273ffffffffffffffffffffffffffffffffffffffff16611d4b82610c96565b73ffffffffffffffffffffffffffffffffffffffff1614611da1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611d9890614cd9565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603611e10576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611e0790614d6b565b60405180910390fd5b611e1d8383836001612433565b8273ffffffffffffffffffffffffffffffffffffffff16611e3d82610c96565b73ffffffffffffffffffffffffffffffffffffffff1614611e93576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611e8a90614cd9565b60405180910390fd5b6004600082815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055506001600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816002600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a461201f8383836001612591565b505050565b60006002600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b6002600a54036120a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161209d90614dd7565b60405180910390fd5b6002600a81905550565b6120ca828260405180602001604052806000815250612597565b5050565b6001600a81905550565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603612146576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161213d90614e43565b60405180910390fd5b80600560008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31836040516122379190612fe7565b60405180910390a3505050565b61224e8284611c96565b61228d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161228490614c47565b60405180910390fd5b612296836125f2565b601460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d1503d148433846040518463ffffffff1660e01b81526004016122f5939291906149a0565b600060405180830381600087803b15801561230f57600080fd5b505af1158015612323573d6000803e3d6000fd5b50505050505050565b612337848484611d2b565b61234384848484612740565b612382576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161237990614ed5565b60405180910390fd5b50505050565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b60008073ffffffffffffffffffffffffffffffffffffffff1661241483612024565b73ffffffffffffffffffffffffffffffffffffffff1614159050919050565b61243f848484846128c7565b6001811115612483576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161247a90614f67565b60405180910390fd5b6000829050600073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16036124ca576124c5816129ed565b612509565b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614612508576125078582612a36565b5b5b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff160361254b5761254681612ba3565b61258a565b8473ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614612589576125888482612c74565b5b5b5050505050565b50505050565b6125a18383612cf3565b6125ae6000848484612740565b6125ed576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016125e490614ed5565b60405180910390fd5b505050565b60006125fd82610c96565b905061260d816000846001612433565b61261682610c96565b90506004600083815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055506002600083815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905581600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a461273c816000846001612591565b5050565b60006127618473ffffffffffffffffffffffffffffffffffffffff16612f10565b156128ba578373ffffffffffffffffffffffffffffffffffffffff1663150b7a0261278a611bd5565b8786866040518563ffffffff1660e01b81526004016127ac9493929190614f87565b6020604051808303816000875af19250505080156127e857506040513d601f19601f820116820180604052508101906127e59190614fe8565b60015b61286a573d8060008114612818576040519150601f19603f3d011682016040523d82523d6000602084013e61281d565b606091505b506000815103612862576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161285990614ed5565b60405180910390fd5b805181602001fd5b63150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149150506128bf565b600190505b949350505050565b60018111156129e757600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161461295b5780600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546129539190615015565b925050819055505b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16146129e65780600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546129de9190615049565b925050819055505b5b50505050565b6008805490506009600083815260200190815260200160002081905550600881908060018154018082558091505060019003906000526020600020016000909190919091505550565b60006001612a4384610d1c565b612a4d9190615015565b9050600060076000848152602001908152602001600020549050818114612b32576000600660008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002054905080600660008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002081905550816007600083815260200190815260200160002081905550505b6007600084815260200190815260200160002060009055600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008381526020019081526020016000206000905550505050565b60006001600880549050612bb79190615015565b9050600060096000848152602001908152602001600020549050600060088381548110612be757612be661460c565b5b906000526020600020015490508060088381548110612c0957612c0861460c565b5b906000526020600020018190555081600960008381526020019081526020016000208190555060096000858152602001908152602001600020600090556008805480612c5857612c5761509f565b5b6001900381819060005260206000200160009055905550505050565b6000612c7f83610d1c565b905081600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002081905550806007600084815260200190815260200160002081905550505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603612d62576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612d599061511a565b60405180910390fd5b612d6b816123f2565b15612dab576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612da290615186565b60405180910390fd5b612db9600083836001612433565b612dc2816123f2565b15612e02576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612df990615186565b60405180910390fd5b6001600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816002600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4612f0c600083836001612591565b5050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000604051905090565b600080fd5b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b612f7c81612f47565b8114612f8757600080fd5b50565b600081359050612f9981612f73565b92915050565b600060208284031215612fb557612fb4612f3d565b5b6000612fc384828501612f8a565b91505092915050565b60008115159050919050565b612fe181612fcc565b82525050565b6000602082019050612ffc6000830184612fd8565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561303c578082015181840152602081019050613021565b8381111561304b576000848401525b50505050565b6000601f19601f8301169050919050565b600061306d82613002565b613077818561300d565b935061308781856020860161301e565b61309081613051565b840191505092915050565b600060208201905081810360008301526130b58184613062565b905092915050565b6000819050919050565b6130d0816130bd565b81146130db57600080fd5b50565b6000813590506130ed816130c7565b92915050565b60006020828403121561310957613108612f3d565b5b6000613117848285016130de565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061314b82613120565b9050919050565b61315b81613140565b82525050565b60006020820190506131766000830184613152565b92915050565b61318581613140565b811461319057600080fd5b50565b6000813590506131a28161317c565b92915050565b600080604083850312156131bf576131be612f3d565b5b60006131cd85828601613193565b92505060206131de858286016130de565b9150509250929050565b6131f1816130bd565b82525050565b600060208201905061320c60008301846131e8565b92915050565b60008060006060848603121561322b5761322a612f3d565b5b600061323986828701613193565b935050602061324a86828701613193565b925050604061325b868287016130de565b9150509250925092565b60006020828403121561327b5761327a612f3d565b5b600061328984828501613193565b91505092915050565b60007fffffff000000000000000000000000000000000000000000000000000000000082169050919050565b6132c781613292565b82525050565b60006020820190506132e260008301846132be565b92915050565b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61332582613051565b810181811067ffffffffffffffff82111715613344576133436132ed565b5b80604052505050565b6000613357612f33565b9050613363828261331c565b919050565b600067ffffffffffffffff821115613383576133826132ed565b5b602082029050602081019050919050565b600080fd5b60006133ac6133a784613368565b61334d565b905080838252602082019050602084028301858111156133cf576133ce613394565b5b835b818110156133f857806133e48882613193565b8452602084019350506020810190506133d1565b5050509392505050565b600082601f830112613417576134166132e8565b5b8135613427848260208601613399565b91505092915050565b600067ffffffffffffffff82111561344b5761344a6132ed565b5b602082029050602081019050919050565b6000819050919050565b61346f8161345c565b811461347a57600080fd5b50565b60008135905061348c81613466565b92915050565b60006134a56134a084613430565b61334d565b905080838252602082019050602084028301858111156134c8576134c7613394565b5b835b818110156134f157806134dd888261347d565b8452602084019350506020810190506134ca565b5050509392505050565b600082601f8301126135105761350f6132e8565b5b8135613520848260208601613492565b91505092915050565b600067ffffffffffffffff821115613544576135436132ed565b5b602082029050602081019050919050565b600061356861356384613529565b61334d565b9050808382526020820190506020840283018581111561358b5761358a613394565b5b835b818110156135b457806135a088826130de565b84526020840193505060208101905061358d565b5050509392505050565b600082601f8301126135d3576135d26132e8565b5b81356135e3848260208601613555565b91505092915050565b600067ffffffffffffffff821115613607576136066132ed565b5b602082029050602081019050919050565b600080fd5b600067ffffffffffffffff821115613638576136376132ed565b5b61364182613051565b9050602081019050919050565b82818337600083830152505050565b600061367061366b8461361d565b61334d565b90508281526020810184848401111561368c5761368b613618565b5b61369784828561364e565b509392505050565b600082601f8301126136b4576136b36132e8565b5b81356136c484826020860161365d565b91505092915050565b60006136e06136db846135ec565b61334d565b9050808382526020820190506020840283018581111561370357613702613394565b5b835b8181101561374a57803567ffffffffffffffff811115613728576137276132e8565b5b808601613735898261369f565b85526020850194505050602081019050613705565b5050509392505050565b600082601f830112613769576137686132e8565b5b81356137798482602086016136cd565b91505092915050565b600067ffffffffffffffff82111561379d5761379c6132ed565b5b6137a682613051565b9050602081019050919050565b60006137c66137c184613782565b61334d565b9050828152602081018484840111156137e2576137e1613618565b5b6137ed84828561364e565b509392505050565b600082601f83011261380a576138096132e8565b5b813561381a8482602086016137b3565b91505092915050565b60008060008060008060c087890312156138405761383f612f3d565b5b600087013567ffffffffffffffff81111561385e5761385d612f42565b5b61386a89828a01613402565b965050602087013567ffffffffffffffff81111561388b5761388a612f42565b5b61389789828a016134fb565b955050604087013567ffffffffffffffff8111156138b8576138b7612f42565b5b6138c489828a016135be565b94505060606138d589828a01613193565b935050608087013567ffffffffffffffff8111156138f6576138f5612f42565b5b61390289828a01613754565b92505060a087013567ffffffffffffffff81111561392357613922612f42565b5b61392f89828a016137f5565b9150509295509295509295565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b613971816130bd565b82525050565b60006139838383613968565b60208301905092915050565b6000602082019050919050565b60006139a78261393c565b6139b18185613947565b93506139bc83613958565b8060005b838110156139ed5781516139d48882613977565b97506139df8361398f565b9250506001810190506139c0565b5085935050505092915050565b60006020820190508181036000830152613a14818461399c565b905092915050565b613a2581612fcc565b8114613a3057600080fd5b50565b600081359050613a4281613a1c565b92915050565b60008060408385031215613a5f57613a5e612f3d565b5b6000613a6d85828601613193565b9250506020613a7e85828601613a33565b9150509250929050565b600080600060608486031215613aa157613aa0612f3d565b5b600084013567ffffffffffffffff811115613abf57613abe612f42565b5b613acb868287016135be565b9350506020613adc86828701613193565b925050604084013567ffffffffffffffff811115613afd57613afc612f42565b5b613b09868287016137f5565b9150509250925092565b60008060008060808587031215613b2d57613b2c612f3d565b5b6000613b3b87828801613193565b9450506020613b4c87828801613193565b9350506040613b5d878288016130de565b925050606085013567ffffffffffffffff811115613b7e57613b7d612f42565b5b613b8a878288016137f5565b91505092959194509250565b600080600080600060a08688031215613bb257613bb1612f3d565b5b6000613bc088828901613193565b9550506020613bd188828901613193565b9450506040613be288828901613193565b9350506060613bf388828901613193565b9250506080613c0488828901613a33565b9150509295509295909350565b60008060408385031215613c2857613c27612f3d565b5b6000613c3685828601613193565b9250506020613c4785828601613193565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680613c9857607f821691505b602082108103613cab57613caa613c51565b5b50919050565b7f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560008201527f7200000000000000000000000000000000000000000000000000000000000000602082015250565b6000613d0d60218361300d565b9150613d1882613cb1565b604082019050919050565b60006020820190508181036000830152613d3c81613d00565b9050919050565b7f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60008201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c000000602082015250565b6000613d9f603d8361300d565b9150613daa82613d43565b604082019050919050565b60006020820190508181036000830152613dce81613d92565b9050919050565b7f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560008201527f72206f7220617070726f76656400000000000000000000000000000000000000602082015250565b6000613e31602d8361300d565b9150613e3c82613dd5565b604082019050919050565b60006020820190508181036000830152613e6081613e24565b9050919050565b7f455243373231456e756d657261626c653a206f776e657220696e646578206f7560008201527f74206f6620626f756e6473000000000000000000000000000000000000000000602082015250565b6000613ec3602b8361300d565b9150613ece82613e67565b604082019050919050565b60006020820190508181036000830152613ef281613eb6565b9050919050565b600081549050919050565b600082825260208201905092915050565b60008190508160005260206000209050919050565b613f3381613292565b82525050565b60008160001c9050919050565b60008160e81b9050919050565b6000613f5e82613f46565b9050919050565b6000613f78613f7383613f39565b613f53565b9050919050565b60008160181c9050919050565b6000613f9f613f9a83613f7f565b613f53565b9050919050565b60008160301c9050919050565b6000613fc6613fc183613fa6565b613f53565b9050919050565b60008160481c9050919050565b6000613fed613fe883613fcd565b613f53565b9050919050565b60008160601c9050919050565b600061401461400f83613ff4565b613f53565b9050919050565b60008160781c9050919050565b600061403b6140368361401b565b613f53565b9050919050565b60008160901c9050919050565b600061406261405d83614042565b613f53565b9050919050565b60008160a81c9050919050565b600061408961408483614069565b613f53565b9050919050565b60008160c01c9050919050565b60006140b06140ab83614090565b613f53565b9050919050565b60008160d81c9050919050565b60006140d76140d2836140b7565b613f53565b9050919050565b60006140e982613ef9565b6140f38185613f04565b9350836140ff84613f15565b600060011561421c575b836001600a038201101561421b57815461412b8861412683613f65565b613f2a565b6020880197506141438861413e83613f8c565b613f2a565b60208801975061415b8861415683613fb3565b613f2a565b6020880197506141738861416e83613fda565b613f2a565b60208801975061418b8861418683614001565b613f2a565b6020880197506141a38861419e83614028565b613f2a565b6020880197506141bb886141b68361404f565b613f2a565b6020880197506141d3886141ce83614076565b613f2a565b6020880197506141eb886141e68361409d565b613f2a565b602088019750614203886141fe836140c4565b613f2a565b60208801975060018301925050600a81019050614109565b5b6001156143ad5781548482101561424c5761423f8861423a83613f65565b613f2a565b6020880197506001820191505b84821015614273576142668861426183613f8c565b613f2a565b6020880197506001820191505b8482101561429a5761428d8861428883613fb3565b613f2a565b6020880197506001820191505b848210156142c1576142b4886142af83613fda565b613f2a565b6020880197506001820191505b848210156142e8576142db886142d683614001565b613f2a565b6020880197506001820191505b8482101561430f57614302886142fd83614028565b613f2a565b6020880197506001820191505b8482101561433657614329886143248361404f565b613f2a565b6020880197506001820191505b8482101561435d576143508861434b83614076565b613f2a565b6020880197506001820191505b8482101561438457614377886143728361409d565b613f2a565b6020880197506001820191505b848210156143ab5761439e88614399836140c4565b613f2a565b6020880197506001820191505b505b8694505050505092915050565b600082825260208201905092915050565b60008190508160005260206000209050919050565b600081546143ed81613c80565b6143f781866143ba565b94506001821660008114614412576001811461442457614457565b60ff1983168652602086019350614457565b61442d856143cb565b60005b8381101561444f57815481890152600182019150602081019050614430565b808801955050505b50505092915050565b600060a08201905061447560008301886131e8565b6144826020830187613152565b818103604083015261449481866140de565b905081810360608301526144a881856143e0565b90506144b76080830184613152565b9695505050505050565b60006144d46144cf8461361d565b61334d565b9050828152602081018484840111156144f0576144ef613618565b5b6144fb84828561301e565b509392505050565b600082601f830112614518576145176132e8565b5b81516145288482602086016144c1565b91505092915050565b60006020828403121561454757614546612f3d565b5b600082015167ffffffffffffffff81111561456557614564612f42565b5b61457184828501614503565b91505092915050565b7f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60008201527f7574206f6620626f756e64730000000000000000000000000000000000000000602082015250565b60006145d6602c8361300d565b91506145e18261457a565b604082019050919050565b60006020820190508181036000830152614605816145c9565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4552433732313a20696e76616c696420746f6b656e2049440000000000000000600082015250565b600061467160188361300d565b915061467c8261463b565b602082019050919050565b600060208201905081810360008301526146a081614664565b9050919050565b7f4552433732313a2061646472657373207a65726f206973206e6f74206120766160008201527f6c6964206f776e65720000000000000000000000000000000000000000000000602082015250565b600061470360298361300d565b915061470e826146a7565b604082019050919050565b60006020820190508181036000830152614732816146f6565b9050919050565b600060408201905061474e60008301856131e8565b61475b6020830184613152565b9392505050565b600081519050614771816130c7565b92915050565b60006020828403121561478d5761478c612f3d565b5b600061479b84828501614762565b91505092915050565b600081519050919050565b60006147ba826147a4565b6147c481856143ba565b93506147d481856020860161301e565b6147dd81613051565b840191505092915050565b600060a0820190506147fd6000830188613152565b61480a60208301876131e8565b61481760408301866131e8565b6148246060830185613152565b818103608083015261483681846147af565b90509695505050505050565b60008151905061485181613a1c565b92915050565b60006020828403121561486d5761486c612f3d565b5b600061487b84828501614842565b91505092915050565b7f7500000000000000000000000000000000000000000000000000000000000000600082015250565b60006148ba60018361300d565b91506148c582614884565b602082019050919050565b600060208201905081810360008301526148e9816148ad565b9050919050565b6148f98161345c565b82525050565b60006080820190506149146000830187613152565b61492160208301866148f0565b61492e60408301856131e8565b81810360608301526149408184613062565b905095945050505050565b60008151905061495a81613466565b92915050565b6000806040838503121561497757614976612f3d565b5b600061498585828601614762565b92505060206149968582860161494b565b9150509250929050565b60006060820190506149b560008301866131e8565b6149c26020830185613152565b81810360408301526149d481846147af565b9050949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000614a18826130bd565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614a4a57614a496149de565b5b600182019050919050565b6000614a6082613120565b9050919050565b614a7081614a55565b82525050565b600060a082019050614a8b60008301886131e8565b614a986020830187613152565b614aa56040830186614a67565b614ab26060830185613152565b8181036080830152614ac481846147af565b90509695505050505050565b7f6600000000000000000000000000000000000000000000000000000000000000600082015250565b6000614b0660018361300d565b9150614b1182614ad0565b602082019050919050565b60006020820190508181036000830152614b3581614af9565b9050919050565b6000608082019050614b5160008301876131e8565b614b5e6020830186613152565b8181036040830152614b7081856140de565b90508181036060830152614b8481846143e0565b905095945050505050565b7f6d00000000000000000000000000000000000000000000000000000000000000600082015250565b6000614bc560018361300d565b9150614bd082614b8f565b602082019050919050565b60006020820190508181036000830152614bf481614bb8565b9050919050565b7f6100000000000000000000000000000000000000000000000000000000000000600082015250565b6000614c3160018361300d565b9150614c3c82614bfb565b602082019050919050565b60006020820190508181036000830152614c6081614c24565b9050919050565b7f4552433732313a207472616e736665722066726f6d20696e636f72726563742060008201527f6f776e6572000000000000000000000000000000000000000000000000000000602082015250565b6000614cc360258361300d565b9150614cce82614c67565b604082019050919050565b60006020820190508181036000830152614cf281614cb6565b9050919050565b7f4552433732313a207472616e7366657220746f20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b6000614d5560248361300d565b9150614d6082614cf9565b604082019050919050565b60006020820190508181036000830152614d8481614d48565b9050919050565b7f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00600082015250565b6000614dc1601f8361300d565b9150614dcc82614d8b565b602082019050919050565b60006020820190508181036000830152614df081614db4565b9050919050565b7f4552433732313a20617070726f766520746f2063616c6c657200000000000000600082015250565b6000614e2d60198361300d565b9150614e3882614df7565b602082019050919050565b60006020820190508181036000830152614e5c81614e20565b9050919050565b7f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560008201527f63656976657220696d706c656d656e7465720000000000000000000000000000602082015250565b6000614ebf60328361300d565b9150614eca82614e63565b604082019050919050565b60006020820190508181036000830152614eee81614eb2565b9050919050565b7f455243373231456e756d657261626c653a20636f6e736563757469766520747260008201527f616e7366657273206e6f7420737570706f727465640000000000000000000000602082015250565b6000614f5160358361300d565b9150614f5c82614ef5565b604082019050919050565b60006020820190508181036000830152614f8081614f44565b9050919050565b6000608082019050614f9c6000830187613152565b614fa96020830186613152565b614fb660408301856131e8565b8181036060830152614fc881846147af565b905095945050505050565b600081519050614fe281612f73565b92915050565b600060208284031215614ffe57614ffd612f3d565b5b600061500c84828501614fd3565b91505092915050565b6000615020826130bd565b915061502b836130bd565b92508282101561503e5761503d6149de565b5b828203905092915050565b6000615054826130bd565b915061505f836130bd565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115615094576150936149de565b5b828201905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b7f4552433732313a206d696e7420746f20746865207a65726f2061646472657373600082015250565b600061510460208361300d565b915061510f826150ce565b602082019050919050565b60006020820190508181036000830152615133816150f7565b9050919050565b7f4552433732313a20746f6b656e20616c7265616479206d696e74656400000000600082015250565b6000615170601c8361300d565b915061517b8261513a565b602082019050919050565b6000602082019050818103600083015261519f81615163565b905091905056fea2646970667358221220e60a118b265bffa0bee1156a3c15658b87605c5886ba72a04c6b3be6591e04c964736f6c634300080e0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000074a8fa105bf658bde876c47d65999ea9622d1ee00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000003ffffff000000000000000000000000000000000000000000000000000000000035a98e00000000000000000000000000000000000000000000000000000000005f5fbc0000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : manager_ (address): 0x074a8fa105Bf658BDE876C47D65999ea9622d1ee
Arg [1] : colors_ (bytes3[]): System.Byte[],System.Byte[],System.Byte[]

-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 000000000000000000000000074a8fa105bf658bde876c47d65999ea9622d1ee
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000040
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [3] : ffffff0000000000000000000000000000000000000000000000000000000000
Arg [4] : 35a98e0000000000000000000000000000000000000000000000000000000000
Arg [5] : 5f5fbc0000000000000000000000000000000000000000000000000000000000


Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.