ETH Price: $3,392.80 (-1.26%)
Gas: 2 Gwei

Contract

0x4e1e18aaCCDf9acFd2E8847654A3871dfD234F02
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

TokenTracker

m

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
0x60806040137549352021-12-06 22:35:57935 days ago1638830157IN
 Create: MergeMetadata
0 ETH0.50888386154.41152187

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
MergeMetadata

Compiler Version
v0.8.6+commit.11564f7e

Optimization Enabled:
Yes with 1000 runs

Other Settings:
default evmVersion
File 1 of 5 : MergeMetadata.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;

/** 
 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX  .***   XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX  ,*********  XXXXXXXXXXXXXXXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX  ***************  XXXXXXXXXXXXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX  .*******************  XXXXXXXXXXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX  ***********    **********  XXXXXXXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX   ***********       ***********  XXXXXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXXXXXXXXXXX  ***********         ***************  XXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXXXXXXXXX  ***********           ****    ********* XXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXXXXXXXXX *********      ***    ***      *********  XXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXXXXXXXXX  **********  *****          *********** XXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXXXXX   /////.*************         ***********  XXXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXX  /////////...***********      ************  XXXXXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXX/ ///////////..... /////////   ///////////   XXXXXXXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXX  /    //////.........///////////////////   XXXXXXXXXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXXX .///////...........//////////////   XXXXXXXXXXXXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXX .///////.....//..////  /////////  XXXXXXXXXXXXXXXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXX# /////////////////////  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXX   ////////////////////   XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 * XXXXXXXXXXXX   ////////////// //////   XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 */

import {ABDKMath64x64} from "../util/ABDKMath64x64.sol";
import {Base64} from "../util/Base64.sol";
import {Roots} from "../util/Roots.sol";
import {Strings} from "../util/Strings.sol";

interface IMergeMetadata {    
    function tokenMetadata(
        uint256 tokenId, 
        uint256 rarity, 
        uint256 tokenMass, 
        uint256 alphaMass, 
        bool isAlpha, 
        uint256 mergeCount) external view returns (string memory);
}

contract MergeMetadata is IMergeMetadata {
    
    struct ERC721MetadataStructure {
        bool isImageLinked;
        string name;
        string description;
        string createdBy;
        string image;
        ERC721MetadataAttribute[] attributes;
    }

    struct ERC721MetadataAttribute {
        bool includeDisplayType;
        bool includeTraitType;
        bool isValueAString;
        string displayType;
        string traitType;
        string value;
    }
    
    using ABDKMath64x64 for int128;    
    using Base64 for string;
    using Roots for uint;    
    using Strings for uint256;    
    
    address public owner;  

    string private _name;
    string private _imageBaseURI;
    string private _imageExtension;
    uint256 private _maxRadius;
    string[] private _imageParts;
    mapping (string => string) private _classStyles;
  
    string constant private _RADIUS_TAG = '<RADIUS>';
    string constant private _CLASS_TAG = '<CLASS>';  
    string constant private _CLASS_STYLE_TAG = '<CLASS_STYLE>';  
  
    constructor() {
        owner = msg.sender;
        _name = "m";
        _imageBaseURI = ""; // Set to empty string - results in on-chain SVG generation by default unless this is set later
        _imageExtension = ""; // Set to empty string - can be changed later to remain empty, .png, .mp4, etc
        _maxRadius = 1000;

        // Deploy with default SVG image parts - can be completely replaced later
        _imageParts.push("<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='2000' height='2000'>");
            _imageParts.push("<style>");
                _imageParts.push(".m1 #c{fill: #fff;}");
                _imageParts.push(".m1 #r{fill: #000;}");
                _imageParts.push(".m2 #c{fill: #fc3;}");
                _imageParts.push(".m2 #r{fill: #000;}");
                _imageParts.push(".m3 #c{fill: #fff;}");
                _imageParts.push(".m3 #r{fill: #33f;}");
                _imageParts.push(".m4 #c{fill: #fff;}");
                _imageParts.push(".m4 #r{fill: #f33;}");
                _imageParts.push(".a #c{fill: #000 !important;}");
                _imageParts.push(".a #r{fill: #fff !important;}");
                _imageParts.push(_CLASS_STYLE_TAG);
            _imageParts.push("</style>");
            _imageParts.push("<g class='");
                _imageParts.push(_CLASS_TAG);
                _imageParts.push("'>");
                    _imageParts.push("<rect id='r' width='2000' height='2000'/>");
                    _imageParts.push("<circle id='c' cx='1000' cy='1000' r='");
                        _imageParts.push(_RADIUS_TAG);
                    _imageParts.push("'/>");
            _imageParts.push("</g>");                
        _imageParts.push("</svg>");
    }        
    
    function setName(string calldata name_) external { 
        _requireOnlyOwner();       
        _name = name_;
    }

    function setImageBaseURI(string calldata imageBaseURI_, string calldata imageExtension_) external {        
        _requireOnlyOwner();
        _imageBaseURI = imageBaseURI_;
        _imageExtension = imageExtension_;
    }

    function setMaxRadius(uint256 maxRadius_) external {
        _requireOnlyOwner();
        _maxRadius = maxRadius_;
    }    

    function tokenMetadata(uint256 tokenId, uint256 rarity, uint256 tokenMass, uint256 alphaMass, bool isAlpha, uint256 mergeCount) external view override returns (string memory) {        
        string memory base64Json = Base64.encode(bytes(string(abi.encodePacked(_getJson(tokenId, rarity, tokenMass, alphaMass, isAlpha, mergeCount)))));
        return string(abi.encodePacked('data:application/json;base64,', base64Json));
    }

    function updateImageParts(string[] memory imageParts_) public {
        _requireOnlyOwner();
        _imageParts = imageParts_;
    }

    function updateClassStyle(string calldata cssClass, string calldata cssStyle) external {
        _requireOnlyOwner();
        _classStyles[cssClass] = cssStyle;
    }

    function getClassStyle(string memory cssClass) public view returns (string memory) {
        return _classStyles[cssClass];
    }

    function name() public view returns (string memory) {
        return _name;
    }

    function imageBaseURI() public view returns (string memory) {
        return _imageBaseURI;
    }

    function imageExtension() public view returns (string memory) {
        return _imageExtension;
    }

    function maxRadius() public view returns (uint256) {
        return _maxRadius;
    }            

    function getClassString(uint256 tokenId, uint256 rarity, bool isAlpha, bool offchainImage) public pure returns (string memory) {
        return _getClassString(tokenId, rarity, isAlpha, offchainImage);
    }

    function _getJson(uint256 tokenId, uint256 rarity, uint256 tokenMass, uint256 alphaMass, bool isAlpha, uint256 mergeCount) private view returns (string memory) {        
        string memory imageData = 
            bytes(_imageBaseURI).length == 0 ? 
                _getSvg(tokenId, rarity, tokenMass, alphaMass, isAlpha) :
                string(abi.encodePacked(imageBaseURI(), _getClassString(tokenId, rarity, isAlpha, true), "_", uint256(int256(_getScaledRadius(tokenMass, alphaMass, _maxRadius).toInt())).toString(), imageExtension()));

        ERC721MetadataStructure memory metadata = ERC721MetadataStructure({
            isImageLinked: bytes(_imageBaseURI).length > 0, 
            name: string(abi.encodePacked(name(), "(", tokenMass.toString(), ") #", tokenId.toString())),
            description: tokenMass.toString(),
            createdBy: "Pak",
            image: imageData,
            attributes: _getJsonAttributes(tokenId, rarity, tokenMass, mergeCount, isAlpha)
        });

        return _generateERC721Metadata(metadata);
    }        

    function _getJsonAttributes(uint256 tokenId, uint256 rarity, uint256 tokenMass, uint256 mergeCount, bool isAlpha) private pure returns (ERC721MetadataAttribute[] memory) {
        uint256 tensDigit = tokenId % 100 / 10;
        uint256 onesDigit = tokenId % 10;
        uint256 class = tensDigit * 10 + onesDigit;

        ERC721MetadataAttribute[] memory metadataAttributes = new ERC721MetadataAttribute[](5);
        metadataAttributes[0] = _getERC721MetadataAttribute(false, true, false, "", "Mass", tokenMass.toString());
        metadataAttributes[1] = _getERC721MetadataAttribute(false, true, false, "", "Alpha", isAlpha ? "1" : "0");
        metadataAttributes[2] = _getERC721MetadataAttribute(false, true, false, "", "Tier", rarity.toString());
        metadataAttributes[3] = _getERC721MetadataAttribute(false, true, false, "", "Class", class.toString());
        metadataAttributes[4] = _getERC721MetadataAttribute(false, true, false, "", "Merges", mergeCount.toString());
        return metadataAttributes;
    }    

    function _getERC721MetadataAttribute(bool includeDisplayType, bool includeTraitType, bool isValueAString, string memory displayType, string memory traitType, string memory value) private pure returns (ERC721MetadataAttribute memory) {
        ERC721MetadataAttribute memory attribute = ERC721MetadataAttribute({
            includeDisplayType: includeDisplayType,
            includeTraitType: includeTraitType,
            isValueAString: isValueAString,
            displayType: displayType,
            traitType: traitType,
            value: value
        });

        return attribute;
    }    

    function _getSvg(uint256 tokenId, uint256 rarity, uint256 tokenMass, uint256 alphaMass, bool isAlpha) private view returns (string memory) {
        bytes memory byteString;
        for (uint i = 0; i < _imageParts.length; i++) {
          if (_checkTag(_imageParts[i], _RADIUS_TAG)) {
            byteString = abi.encodePacked(byteString, _floatToString(_getScaledRadius(tokenMass, alphaMass, _maxRadius)));
          } else if (_checkTag(_imageParts[i], _CLASS_TAG)) {
            byteString = abi.encodePacked(byteString, _getClassString(tokenId, rarity, isAlpha, false));
          } else if (_checkTag(_imageParts[i], _CLASS_STYLE_TAG)) {
              uint256 tensDigit = tokenId % 100 / 10;
              uint256 onesDigit = tokenId % 10;
              uint256 class = tensDigit * 10 + onesDigit;
              string memory classCss = getClassStyle(_getTokenIdClass(class));
              if(bytes(classCss).length > 0) {
                  byteString = abi.encodePacked(byteString, classCss);
              }            
          } else {
            byteString = abi.encodePacked(byteString, _imageParts[i]);
          }
        }
        return string(byteString); 
    }

    function _getScaledRadius(uint256 tokenMass, uint256 alphaMass, uint256 maximumRadius) private pure returns (int128) {
        int128 radiusMass = _getRadius64x64(tokenMass);
        int128 radiusAlphaMass = _getRadius64x64(alphaMass);
        int128 scalePercentage = ABDKMath64x64.div(radiusMass, radiusAlphaMass);                
        int128 scaledRadius = ABDKMath64x64.mul(ABDKMath64x64.fromUInt(maximumRadius), scalePercentage);
        if(uint256(int256(scaledRadius.toInt())) == 0) {
            scaledRadius = ABDKMath64x64.fromUInt(1);
        }
        return scaledRadius;
    }

    // Radius = Cube Root(Mass) * Cube Root (0.23873241463)
    // Radius = Cube Root(Mass) * 0.62035049089
    function _getRadius64x64(uint256 mass) private pure returns (int128) {        
        int128 cubeRootScalar = ABDKMath64x64.divu(62035049089, 100000000000);
        int128 cubeRootMass = ABDKMath64x64.divu(mass.nthRoot(3, 6, 32), 1000000);
        int128 radius = ABDKMath64x64.mul(cubeRootMass, cubeRootScalar);        
        return radius;
    }            

    function _generateERC721Metadata(ERC721MetadataStructure memory metadata) private pure returns (string memory) {
      bytes memory byteString;    
    
        byteString = abi.encodePacked(
          byteString,
          _openJsonObject());
    
        byteString = abi.encodePacked(
          byteString,
          _pushJsonPrimitiveStringAttribute("name", metadata.name, true));
    
        byteString = abi.encodePacked(
          byteString,
          _pushJsonPrimitiveStringAttribute("description", metadata.description, true));
    
        byteString = abi.encodePacked(
          byteString,
          _pushJsonPrimitiveStringAttribute("created_by", metadata.createdBy, true));
    
        if(metadata.isImageLinked) {
            byteString = abi.encodePacked(
                byteString,
                _pushJsonPrimitiveStringAttribute("image", metadata.image, true));
        } else {
            byteString = abi.encodePacked(
                byteString,
                _pushJsonPrimitiveStringAttribute("image_data", metadata.image, true));
        }

        byteString = abi.encodePacked(
          byteString,
          _pushJsonComplexAttribute("attributes", _getAttributes(metadata.attributes), false));
    
        byteString = abi.encodePacked(
          byteString,
          _closeJsonObject());
    
        return string(byteString);
    }

    function _getAttributes(ERC721MetadataAttribute[] memory attributes) private pure returns (string memory) {
        bytes memory byteString;
    
        byteString = abi.encodePacked(
          byteString,
          _openJsonArray());
    
        for (uint i = 0; i < attributes.length; i++) {
          ERC721MetadataAttribute memory attribute = attributes[i];

          byteString = abi.encodePacked(
            byteString,
            _pushJsonArrayElement(_getAttribute(attribute), i < (attributes.length - 1)));
        }
    
        byteString = abi.encodePacked(
          byteString,
          _closeJsonArray());
    
        return string(byteString);
    }

    function _getAttribute(ERC721MetadataAttribute memory attribute) private pure returns (string memory) {
        bytes memory byteString;
        
        byteString = abi.encodePacked(
          byteString,
          _openJsonObject());
    
        if(attribute.includeDisplayType) {
          byteString = abi.encodePacked(
            byteString,
            _pushJsonPrimitiveStringAttribute("display_type", attribute.displayType, true));
        }
    
        if(attribute.includeTraitType) {
          byteString = abi.encodePacked(
            byteString,
            _pushJsonPrimitiveStringAttribute("trait_type", attribute.traitType, true));
        }
    
        if(attribute.isValueAString) {
          byteString = abi.encodePacked(
            byteString,
            _pushJsonPrimitiveStringAttribute("value", attribute.value, false));
        } else {
          byteString = abi.encodePacked(
            byteString,
            _pushJsonPrimitiveNonStringAttribute("value", attribute.value, false));
        }
    
        byteString = abi.encodePacked(
          byteString,
          _closeJsonObject());
    
        return string(byteString);
    }

    function _getClassString(uint256 tokenId, uint256 rarity, bool isAlpha, bool offchainImage) private pure returns (string memory) {
        bytes memory byteString;    
    
        byteString = abi.encodePacked(byteString, _getRarityClass(rarity));
        
        if(isAlpha) {
            byteString = abi.encodePacked(
              byteString,
              string(abi.encodePacked(offchainImage ? "_" : " ", "a")));
        }

        uint256 tensDigit = tokenId % 100 / 10;
        uint256 onesDigit = tokenId % 10;
        uint256 class = tensDigit * 10 + onesDigit;

        byteString = abi.encodePacked(
          byteString,
          string(abi.encodePacked(offchainImage ? "_" : " ", _getTokenIdClass(class))));

        return string(byteString);    
    }

    function _getRarityClass(uint256 rarity) private pure returns (string memory) {
        return string(abi.encodePacked("m", rarity.toString()));
    }

    function _getTokenIdClass(uint256 class) private pure returns (string memory) {
        return string(abi.encodePacked("c", class.toString()));
    }

    function _checkTag(string storage a, string memory b) private pure returns (bool) {
        return (keccak256(abi.encodePacked((a))) == keccak256(abi.encodePacked((b))));
    }

    function _floatToString(int128 value) private pure returns (string memory) {
        uint256 decimal4 = (value & 0xFFFFFFFFFFFFFFFF).mulu(10000);
        return string(abi.encodePacked(uint256(int256(value.toInt())).toString(), '.', _decimal4ToString(decimal4)));
    }
  
    function _decimal4ToString(uint256 decimal4) private pure returns (string memory) {
        bytes memory decimal4Characters = new bytes(4);
        for (uint i = 0; i < 4; i++) {
          decimal4Characters[3 - i] = bytes1(uint8(0x30 + decimal4 % 10));
          decimal4 /= 10;
        }
        return string(abi.encodePacked(decimal4Characters));
    }

    function _requireOnlyOwner() private view {
        require(msg.sender == owner, "You are not the owner");
    }

    function _openJsonObject() private pure returns (string memory) {        
        return string(abi.encodePacked("{"));
    }

    function _closeJsonObject() private pure returns (string memory) {
        return string(abi.encodePacked("}"));
    }

    function _openJsonArray() private pure returns (string memory) {        
        return string(abi.encodePacked("["));
    }

    function _closeJsonArray() private pure returns (string memory) {        
        return string(abi.encodePacked("]"));
    }

    function _pushJsonPrimitiveStringAttribute(string memory key, string memory value, bool insertComma) private pure returns (string memory) {
        return string(abi.encodePacked('"', key, '": "', value, '"', insertComma ? ',' : ''));
    }

    function _pushJsonPrimitiveNonStringAttribute(string memory key, string memory value, bool insertComma) private pure returns (string memory) {
        return string(abi.encodePacked('"', key, '": ', value, insertComma ? ',' : ''));
    }

    function _pushJsonComplexAttribute(string memory key, string memory value, bool insertComma) private pure returns (string memory) {
        return string(abi.encodePacked('"', key, '": ', value, insertComma ? ',' : ''));
    }

    function _pushJsonArrayElement(string memory value, bool insertComma) private pure returns (string memory) {
        return string(abi.encodePacked(value, insertComma ? ',' : ''));
    }
}

File 2 of 5 : ABDKMath64x64.sol
// SPDX-License-Identifier: BSD-4-Clause
/*
 * ABDK Math 64.64 Smart Contract Library.  Copyright © 2019 by ABDK Consulting.
 * Author: Mikhail Vladimirov <[email protected]>
 */
pragma solidity ^0.8.6;

/**
 * Smart contract library of mathematical functions operating with signed
 * 64.64-bit fixed point numbers.  Signed 64.64-bit fixed point number is
 * basically a simple fraction whose numerator is signed 128-bit integer and
 * denominator is 2^64.  As long as denominator is always the same, there is no
 * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are
 * represented by int128 type holding only the numerator.
 */
library ABDKMath64x64 {
  /*
   * Minimum value signed 64.64-bit fixed point number may have. 
   */
  int128 private constant MIN_64x64 = -0x80000000000000000000000000000000;

  /*
   * Maximum value signed 64.64-bit fixed point number may have. 
   */
  int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

  /**
   * Convert signed 256-bit integer number into signed 64.64-bit fixed point
   * number.  Revert on overflow.
   *
   * @param x signed 256-bit integer number
   * @return signed 64.64-bit fixed point number
   */
  function fromInt (int256 x) internal pure returns (int128) {
    unchecked {
      require (x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF);
      return int128 (x << 64);
    }
  }

  /**
   * Convert signed 64.64 fixed point number into signed 64-bit integer number
   * rounding down.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64-bit integer number
   */
  function toInt (int128 x) internal pure returns (int64) {
    unchecked {
      return int64 (x >> 64);
    }
  }

  /**
   * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point
   * number.  Revert on overflow.
   *
   * @param x unsigned 256-bit integer number
   * @return signed 64.64-bit fixed point number
   */
  function fromUInt (uint256 x) internal pure returns (int128) {
    unchecked {
      require (x <= 0x7FFFFFFFFFFFFFFF);
      return int128 (int256 (x << 64));
    }
  }

  /**
   * Convert signed 64.64 fixed point number into unsigned 64-bit integer
   * number rounding down.  Revert on underflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @return unsigned 64-bit integer number
   */
  function toUInt (int128 x) internal pure returns (uint64) {
    unchecked {
      require (x >= 0);
      return uint64 (uint128 (x >> 64));
    }
  }

  /**
   * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point
   * number rounding down.  Revert on overflow.
   *
   * @param x signed 128.128-bin fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function from128x128 (int256 x) internal pure returns (int128) {
    unchecked {
      int256 result = x >> 64;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Convert signed 64.64 fixed point number into signed 128.128 fixed point
   * number.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 128.128 fixed point number
   */
  function to128x128 (int128 x) internal pure returns (int256) {
    unchecked {
      return int256 (x) << 64;
    }
  }

  /**
   * Calculate x + y.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function add (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      int256 result = int256(x) + y;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate x - y.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function sub (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      int256 result = int256(x) - y;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate x * y rounding down.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function mul (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      int256 result = int256(x) * y >> 64;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point
   * number and y is signed 256-bit integer number.  Revert on overflow.
   *
   * @param x signed 64.64 fixed point number
   * @param y signed 256-bit integer number
   * @return signed 256-bit integer number
   */
  function muli (int128 x, int256 y) internal pure returns (int256) {
    unchecked {
      if (x == MIN_64x64) {
        require (y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF &&
          y <= 0x1000000000000000000000000000000000000000000000000);
        return -y << 63;
      } else {
        bool negativeResult = false;
        if (x < 0) {
          x = -x;
          negativeResult = true;
        }
        if (y < 0) {
          y = -y; // We rely on overflow behavior here
          negativeResult = !negativeResult;
        }
        uint256 absoluteResult = mulu (x, uint256 (y));
        if (negativeResult) {
          require (absoluteResult <=
            0x8000000000000000000000000000000000000000000000000000000000000000);
          return -int256 (absoluteResult); // We rely on overflow behavior here
        } else {
          require (absoluteResult <=
            0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
          return int256 (absoluteResult);
        }
      }
    }
  }

  /**
   * Calculate x * y rounding down, where x is signed 64.64 fixed point number
   * and y is unsigned 256-bit integer number.  Revert on overflow.
   *
   * @param x signed 64.64 fixed point number
   * @param y unsigned 256-bit integer number
   * @return unsigned 256-bit integer number
   */
  function mulu (int128 x, uint256 y) internal pure returns (uint256) {
    unchecked {
      if (y == 0) return 0;

      require (x >= 0);

      uint256 lo = (uint256 (int256 (x)) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64;
      uint256 hi = uint256 (int256 (x)) * (y >> 128);

      require (hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
      hi <<= 64;

      require (hi <=
        0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - lo);
      return hi + lo;
    }
  }

  /**
   * Calculate x / y rounding towards zero.  Revert on overflow or when y is
   * zero.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function div (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      require (y != 0);
      int256 result = (int256 (x) << 64) / y;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate x / y rounding towards zero, where x and y are signed 256-bit
   * integer numbers.  Revert on overflow or when y is zero.
   *
   * @param x signed 256-bit integer number
   * @param y signed 256-bit integer number
   * @return signed 64.64-bit fixed point number
   */
  function divi (int256 x, int256 y) internal pure returns (int128) {
    unchecked {
      require (y != 0);

      bool negativeResult = false;
      if (x < 0) {
        x = -x; // We rely on overflow behavior here
        negativeResult = true;
      }
      if (y < 0) {
        y = -y; // We rely on overflow behavior here
        negativeResult = !negativeResult;
      }
      uint128 absoluteResult = divuu (uint256 (x), uint256 (y));
      if (negativeResult) {
        require (absoluteResult <= 0x80000000000000000000000000000000);
        return -int128 (absoluteResult); // We rely on overflow behavior here
      } else {
        require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
        return int128 (absoluteResult); // We rely on overflow behavior here
      }
    }
  }

  /**
   * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit
   * integer numbers.  Revert on overflow or when y is zero.
   *
   * @param x unsigned 256-bit integer number
   * @param y unsigned 256-bit integer number
   * @return signed 64.64-bit fixed point number
   */
  function divu (uint256 x, uint256 y) internal pure returns (int128) {
    unchecked {
      require (y != 0);
      uint128 result = divuu (x, y);
      require (result <= uint128 (MAX_64x64));
      return int128 (result);
    }
  }

  /**
   * Calculate -x.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function neg (int128 x) internal pure returns (int128) {
    unchecked {
      require (x != MIN_64x64);
      return -x;
    }
  }

  /**
   * Calculate |x|.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function abs (int128 x) internal pure returns (int128) {
    unchecked {
      require (x != MIN_64x64);
      return x < 0 ? -x : x;
    }
  }

  /**
   * Calculate 1 / x rounding towards zero.  Revert on overflow or when x is
   * zero.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function inv (int128 x) internal pure returns (int128) {
    unchecked {
      require (x != 0);
      int256 result = int256 (0x100000000000000000000000000000000) / x;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function avg (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      return int128 ((int256 (x) + int256 (y)) >> 1);
    }
  }

  /**
   * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down.
   * Revert on overflow or in case x * y is negative.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function gavg (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      int256 m = int256 (x) * int256 (y);
      require (m >= 0);
      require (m <
          0x4000000000000000000000000000000000000000000000000000000000000000);
      return int128 (sqrtu (uint256 (m)));
    }
  }

  /**
   * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number
   * and y is unsigned 256-bit integer number.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y uint256 value
   * @return signed 64.64-bit fixed point number
   */
  function pow (int128 x, uint256 y) internal pure returns (int128) {
    unchecked {
      bool negative = x < 0 && y & 1 == 1;

      uint256 absX = uint128 (x < 0 ? -x : x);
      uint256 absResult;
      absResult = 0x100000000000000000000000000000000;

      if (absX <= 0x10000000000000000) {
        absX <<= 63;
        while (y != 0) {
          if (y & 0x1 != 0) {
            absResult = absResult * absX >> 127;
          }
          absX = absX * absX >> 127;

          if (y & 0x2 != 0) {
            absResult = absResult * absX >> 127;
          }
          absX = absX * absX >> 127;

          if (y & 0x4 != 0) {
            absResult = absResult * absX >> 127;
          }
          absX = absX * absX >> 127;

          if (y & 0x8 != 0) {
            absResult = absResult * absX >> 127;
          }
          absX = absX * absX >> 127;

          y >>= 4;
        }

        absResult >>= 64;
      } else {
        uint256 absXShift = 63;
        if (absX < 0x1000000000000000000000000) { absX <<= 32; absXShift -= 32; }
        if (absX < 0x10000000000000000000000000000) { absX <<= 16; absXShift -= 16; }
        if (absX < 0x1000000000000000000000000000000) { absX <<= 8; absXShift -= 8; }
        if (absX < 0x10000000000000000000000000000000) { absX <<= 4; absXShift -= 4; }
        if (absX < 0x40000000000000000000000000000000) { absX <<= 2; absXShift -= 2; }
        if (absX < 0x80000000000000000000000000000000) { absX <<= 1; absXShift -= 1; }

        uint256 resultShift = 0;
        while (y != 0) {
          require (absXShift < 64);

          if (y & 0x1 != 0) {
            absResult = absResult * absX >> 127;
            resultShift += absXShift;
            if (absResult > 0x100000000000000000000000000000000) {
              absResult >>= 1;
              resultShift += 1;
            }
          }
          absX = absX * absX >> 127;
          absXShift <<= 1;
          if (absX >= 0x100000000000000000000000000000000) {
              absX >>= 1;
              absXShift += 1;
          }

          y >>= 1;
        }

        require (resultShift < 64);
        absResult >>= 64 - resultShift;
      }
      int256 result = negative ? -int256 (absResult) : int256 (absResult);
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate sqrt (x) rounding down.  Revert if x < 0.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function sqrt (int128 x) internal pure returns (int128) {
    unchecked {
      require (x >= 0);
      return int128 (sqrtu (uint256 (int256 (x)) << 64));
    }
  }

  /**
   * Calculate binary logarithm of x.  Revert if x <= 0.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function log_2 (int128 x) internal pure returns (int128) {
    unchecked {
      require (x > 0);

      int256 msb = 0;
      int256 xc = x;
      if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; }
      if (xc >= 0x100000000) { xc >>= 32; msb += 32; }
      if (xc >= 0x10000) { xc >>= 16; msb += 16; }
      if (xc >= 0x100) { xc >>= 8; msb += 8; }
      if (xc >= 0x10) { xc >>= 4; msb += 4; }
      if (xc >= 0x4) { xc >>= 2; msb += 2; }
      if (xc >= 0x2) msb += 1;  // No need to shift xc anymore

      int256 result = msb - 64 << 64;
      uint256 ux = uint256 (int256 (x)) << uint256 (127 - msb);
      for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) {
        ux *= ux;
        uint256 b = ux >> 255;
        ux >>= 127 + b;
        result += bit * int256 (b);
      }

      return int128 (result);
    }
  }

  /**
   * Calculate natural logarithm of x.  Revert if x <= 0.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function ln (int128 x) internal pure returns (int128) {
    unchecked {
      require (x > 0);

      return int128 (int256 (
          uint256 (int256 (log_2 (x))) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF >> 128));
    }
  }

  /**
   * Calculate binary exponent of x.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function exp_2 (int128 x) internal pure returns (int128) {
    unchecked {
      require (x < 0x400000000000000000); // Overflow

      if (x < -0x400000000000000000) return 0; // Underflow

      uint256 result = 0x80000000000000000000000000000000;

      if (x & 0x8000000000000000 > 0)
        result = result * 0x16A09E667F3BCC908B2FB1366EA957D3E >> 128;
      if (x & 0x4000000000000000 > 0)
        result = result * 0x1306FE0A31B7152DE8D5A46305C85EDEC >> 128;
      if (x & 0x2000000000000000 > 0)
        result = result * 0x1172B83C7D517ADCDF7C8C50EB14A791F >> 128;
      if (x & 0x1000000000000000 > 0)
        result = result * 0x10B5586CF9890F6298B92B71842A98363 >> 128;
      if (x & 0x800000000000000 > 0)
        result = result * 0x1059B0D31585743AE7C548EB68CA417FD >> 128;
      if (x & 0x400000000000000 > 0)
        result = result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8 >> 128;
      if (x & 0x200000000000000 > 0)
        result = result * 0x10163DA9FB33356D84A66AE336DCDFA3F >> 128;
      if (x & 0x100000000000000 > 0)
        result = result * 0x100B1AFA5ABCBED6129AB13EC11DC9543 >> 128;
      if (x & 0x80000000000000 > 0)
        result = result * 0x10058C86DA1C09EA1FF19D294CF2F679B >> 128;
      if (x & 0x40000000000000 > 0)
        result = result * 0x1002C605E2E8CEC506D21BFC89A23A00F >> 128;
      if (x & 0x20000000000000 > 0)
        result = result * 0x100162F3904051FA128BCA9C55C31E5DF >> 128;
      if (x & 0x10000000000000 > 0)
        result = result * 0x1000B175EFFDC76BA38E31671CA939725 >> 128;
      if (x & 0x8000000000000 > 0)
        result = result * 0x100058BA01FB9F96D6CACD4B180917C3D >> 128;
      if (x & 0x4000000000000 > 0)
        result = result * 0x10002C5CC37DA9491D0985C348C68E7B3 >> 128;
      if (x & 0x2000000000000 > 0)
        result = result * 0x1000162E525EE054754457D5995292026 >> 128;
      if (x & 0x1000000000000 > 0)
        result = result * 0x10000B17255775C040618BF4A4ADE83FC >> 128;
      if (x & 0x800000000000 > 0)
        result = result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB >> 128;
      if (x & 0x400000000000 > 0)
        result = result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9 >> 128;
      if (x & 0x200000000000 > 0)
        result = result * 0x10000162E43F4F831060E02D839A9D16D >> 128;
      if (x & 0x100000000000 > 0)
        result = result * 0x100000B1721BCFC99D9F890EA06911763 >> 128;
      if (x & 0x80000000000 > 0)
        result = result * 0x10000058B90CF1E6D97F9CA14DBCC1628 >> 128;
      if (x & 0x40000000000 > 0)
        result = result * 0x1000002C5C863B73F016468F6BAC5CA2B >> 128;
      if (x & 0x20000000000 > 0)
        result = result * 0x100000162E430E5A18F6119E3C02282A5 >> 128;
      if (x & 0x10000000000 > 0)
        result = result * 0x1000000B1721835514B86E6D96EFD1BFE >> 128;
      if (x & 0x8000000000 > 0)
        result = result * 0x100000058B90C0B48C6BE5DF846C5B2EF >> 128;
      if (x & 0x4000000000 > 0)
        result = result * 0x10000002C5C8601CC6B9E94213C72737A >> 128;
      if (x & 0x2000000000 > 0)
        result = result * 0x1000000162E42FFF037DF38AA2B219F06 >> 128;
      if (x & 0x1000000000 > 0)
        result = result * 0x10000000B17217FBA9C739AA5819F44F9 >> 128;
      if (x & 0x800000000 > 0)
        result = result * 0x1000000058B90BFCDEE5ACD3C1CEDC823 >> 128;
      if (x & 0x400000000 > 0)
        result = result * 0x100000002C5C85FE31F35A6A30DA1BE50 >> 128;
      if (x & 0x200000000 > 0)
        result = result * 0x10000000162E42FF0999CE3541B9FFFCF >> 128;
      if (x & 0x100000000 > 0)
        result = result * 0x100000000B17217F80F4EF5AADDA45554 >> 128;
      if (x & 0x80000000 > 0)
        result = result * 0x10000000058B90BFBF8479BD5A81B51AD >> 128;
      if (x & 0x40000000 > 0)
        result = result * 0x1000000002C5C85FDF84BD62AE30A74CC >> 128;
      if (x & 0x20000000 > 0)
        result = result * 0x100000000162E42FEFB2FED257559BDAA >> 128;
      if (x & 0x10000000 > 0)
        result = result * 0x1000000000B17217F7D5A7716BBA4A9AE >> 128;
      if (x & 0x8000000 > 0)
        result = result * 0x100000000058B90BFBE9DDBAC5E109CCE >> 128;
      if (x & 0x4000000 > 0)
        result = result * 0x10000000002C5C85FDF4B15DE6F17EB0D >> 128;
      if (x & 0x2000000 > 0)
        result = result * 0x1000000000162E42FEFA494F1478FDE05 >> 128;
      if (x & 0x1000000 > 0)
        result = result * 0x10000000000B17217F7D20CF927C8E94C >> 128;
      if (x & 0x800000 > 0)
        result = result * 0x1000000000058B90BFBE8F71CB4E4B33D >> 128;
      if (x & 0x400000 > 0)
        result = result * 0x100000000002C5C85FDF477B662B26945 >> 128;
      if (x & 0x200000 > 0)
        result = result * 0x10000000000162E42FEFA3AE53369388C >> 128;
      if (x & 0x100000 > 0)
        result = result * 0x100000000000B17217F7D1D351A389D40 >> 128;
      if (x & 0x80000 > 0)
        result = result * 0x10000000000058B90BFBE8E8B2D3D4EDE >> 128;
      if (x & 0x40000 > 0)
        result = result * 0x1000000000002C5C85FDF4741BEA6E77E >> 128;
      if (x & 0x20000 > 0)
        result = result * 0x100000000000162E42FEFA39FE95583C2 >> 128;
      if (x & 0x10000 > 0)
        result = result * 0x1000000000000B17217F7D1CFB72B45E1 >> 128;
      if (x & 0x8000 > 0)
        result = result * 0x100000000000058B90BFBE8E7CC35C3F0 >> 128;
      if (x & 0x4000 > 0)
        result = result * 0x10000000000002C5C85FDF473E242EA38 >> 128;
      if (x & 0x2000 > 0)
        result = result * 0x1000000000000162E42FEFA39F02B772C >> 128;
      if (x & 0x1000 > 0)
        result = result * 0x10000000000000B17217F7D1CF7D83C1A >> 128;
      if (x & 0x800 > 0)
        result = result * 0x1000000000000058B90BFBE8E7BDCBE2E >> 128;
      if (x & 0x400 > 0)
        result = result * 0x100000000000002C5C85FDF473DEA871F >> 128;
      if (x & 0x200 > 0)
        result = result * 0x10000000000000162E42FEFA39EF44D91 >> 128;
      if (x & 0x100 > 0)
        result = result * 0x100000000000000B17217F7D1CF79E949 >> 128;
      if (x & 0x80 > 0)
        result = result * 0x10000000000000058B90BFBE8E7BCE544 >> 128;
      if (x & 0x40 > 0)
        result = result * 0x1000000000000002C5C85FDF473DE6ECA >> 128;
      if (x & 0x20 > 0)
        result = result * 0x100000000000000162E42FEFA39EF366F >> 128;
      if (x & 0x10 > 0)
        result = result * 0x1000000000000000B17217F7D1CF79AFA >> 128;
      if (x & 0x8 > 0)
        result = result * 0x100000000000000058B90BFBE8E7BCD6D >> 128;
      if (x & 0x4 > 0)
        result = result * 0x10000000000000002C5C85FDF473DE6B2 >> 128;
      if (x & 0x2 > 0)
        result = result * 0x1000000000000000162E42FEFA39EF358 >> 128;
      if (x & 0x1 > 0)
        result = result * 0x10000000000000000B17217F7D1CF79AB >> 128;

      result >>= uint256 (int256 (63 - (x >> 64)));
      require (result <= uint256 (int256 (MAX_64x64)));

      return int128 (int256 (result));
    }
  }

  /**
   * Calculate natural exponent of x.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function exp (int128 x) internal pure returns (int128) {
    unchecked {
      require (x < 0x400000000000000000); // Overflow

      if (x < -0x400000000000000000) return 0; // Underflow

      return exp_2 (
          int128 (int256 (x) * 0x171547652B82FE1777D0FFDA0D23A7D12 >> 128));
    }
  }

  /**
   * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit
   * integer numbers.  Revert on overflow or when y is zero.
   *
   * @param x unsigned 256-bit integer number
   * @param y unsigned 256-bit integer number
   * @return unsigned 64.64-bit fixed point number
   */
  function divuu (uint256 x, uint256 y) private pure returns (uint128) {
    unchecked {
      require (y != 0);

      uint256 result;

      if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
        result = (x << 64) / y;
      else {
        uint256 msb = 192;
        uint256 xc = x >> 192;
        if (xc >= 0x100000000) { xc >>= 32; msb += 32; }
        if (xc >= 0x10000) { xc >>= 16; msb += 16; }
        if (xc >= 0x100) { xc >>= 8; msb += 8; }
        if (xc >= 0x10) { xc >>= 4; msb += 4; }
        if (xc >= 0x4) { xc >>= 2; msb += 2; }
        if (xc >= 0x2) msb += 1;  // No need to shift xc anymore

        result = (x << 255 - msb) / ((y - 1 >> msb - 191) + 1);
        require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

        uint256 hi = result * (y >> 128);
        uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

        uint256 xh = x >> 192;
        uint256 xl = x << 64;

        if (xl < lo) xh -= 1;
        xl -= lo; // We rely on overflow behavior here
        lo = hi << 128;
        if (xl < lo) xh -= 1;
        xl -= lo; // We rely on overflow behavior here

        assert (xh == hi >> 128);

        result += xl / y;
      }

      require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
      return uint128 (result);
    }
  }

  /**
   * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer
   * number.
   *
   * @param x unsigned 256-bit integer number
   * @return unsigned 128-bit integer number
   */
  function sqrtu (uint256 x) private pure returns (uint128) {
    unchecked {
      if (x == 0) return 0;
      else {
        uint256 xx = x;
        uint256 r = 1;
        if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; r <<= 64; }
        if (xx >= 0x10000000000000000) { xx >>= 64; r <<= 32; }
        if (xx >= 0x100000000) { xx >>= 32; r <<= 16; }
        if (xx >= 0x10000) { xx >>= 16; r <<= 8; }
        if (xx >= 0x100) { xx >>= 8; r <<= 4; }
        if (xx >= 0x10) { xx >>= 4; r <<= 2; }
        if (xx >= 0x8) { r <<= 1; }
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1; // Seven iterations should be enough
        uint256 r1 = x / r;
        return uint128 (r < r1 ? r : r1);
      }
    }
  }
}

File 3 of 5 : Base64.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0;

/// @title Base64
/// @author Brecht Devos - <[email protected]>
/// @notice Provides functions for encoding/decoding base64
library Base64 {
    string internal constant TABLE_ENCODE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
    bytes  internal constant TABLE_DECODE = hex"0000000000000000000000000000000000000000000000000000000000000000"
                                            hex"00000000000000000000003e0000003f3435363738393a3b3c3d000000000000"
                                            hex"00000102030405060708090a0b0c0d0e0f101112131415161718190000000000"
                                            hex"001a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132330000000000";

    function encode(bytes memory data) internal pure returns (string memory) {
        if (data.length == 0) return '';

        // load the table into memory
        string memory table = TABLE_ENCODE;

        // multiply by 4/3 rounded up
        uint256 encodedLen = 4 * ((data.length + 2) / 3);

        // add some extra buffer at the end required for the writing
        string memory result = new string(encodedLen + 32);

        assembly {
            // set the actual output length
            mstore(result, encodedLen)

            // prepare the lookup table
            let tablePtr := add(table, 1)

            // input ptr
            let dataPtr := data
            let endPtr := add(dataPtr, mload(data))

            // result ptr, jump over length
            let resultPtr := add(result, 32)

            // run over the input, 3 bytes at a time
            for {} lt(dataPtr, endPtr) {}
            {
                // read 3 bytes
                dataPtr := add(dataPtr, 3)
                let input := mload(dataPtr)

                // write 4 characters
                mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
                resultPtr := add(resultPtr, 1)
                mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
                resultPtr := add(resultPtr, 1)
                mstore8(resultPtr, mload(add(tablePtr, and(shr( 6, input), 0x3F))))
                resultPtr := add(resultPtr, 1)
                mstore8(resultPtr, mload(add(tablePtr, and(        input,  0x3F))))
                resultPtr := add(resultPtr, 1)
            }

            // padding with '='
            switch mod(mload(data), 3)
            case 1 { mstore(sub(resultPtr, 2), shl(240, 0x3d3d)) }
            case 2 { mstore(sub(resultPtr, 1), shl(248, 0x3d)) }
        }

        return result;
    }

    function decode(string memory _data) internal pure returns (bytes memory) {
        bytes memory data = bytes(_data);

        if (data.length == 0) return new bytes(0);
        require(data.length % 4 == 0, "invalid base64 decoder input");

        // load the table into memory
        bytes memory table = TABLE_DECODE;

        // every 4 characters represent 3 bytes
        uint256 decodedLen = (data.length / 4) * 3;

        // add some extra buffer at the end required for the writing
        bytes memory result = new bytes(decodedLen + 32);

        assembly {
            // padding with '='
            let lastBytes := mload(add(data, mload(data)))
            if eq(and(lastBytes, 0xFF), 0x3d) {
                decodedLen := sub(decodedLen, 1)
                if eq(and(lastBytes, 0xFFFF), 0x3d3d) {
                    decodedLen := sub(decodedLen, 1)
                }
            }

            // set the actual output length
            mstore(result, decodedLen)

            // prepare the lookup table
            let tablePtr := add(table, 1)

            // input ptr
            let dataPtr := data
            let endPtr := add(dataPtr, mload(data))

            // result ptr, jump over length
            let resultPtr := add(result, 32)

            // run over the input, 4 characters at a time
            for {} lt(dataPtr, endPtr) {}
            {
               // read 4 characters
               dataPtr := add(dataPtr, 4)
               let input := mload(dataPtr)

               // write 3 bytes
               let output := add(
                   add(
                       shl(18, and(mload(add(tablePtr, and(shr(24, input), 0xFF))), 0xFF)),
                       shl(12, and(mload(add(tablePtr, and(shr(16, input), 0xFF))), 0xFF))),
                   add(
                       shl( 6, and(mload(add(tablePtr, and(shr( 8, input), 0xFF))), 0xFF)),
                               and(mload(add(tablePtr, and(        input , 0xFF))), 0xFF)
                    )
                )
                mstore(resultPtr, shl(232, output))
                resultPtr := add(resultPtr, 3)
            }
        }

        return result;
    }
}

File 4 of 5 : Roots.sol
pragma solidity ^0.8.6;

library Roots {

// calculates a^(1/n) to dp decimal places
    // maxIts bounds the number of iterations performed
    function nthRoot(uint _a, uint _n, uint _dp, uint _maxIts) pure internal returns(uint) {
        assert (_n > 1);

        // The scale factor is a crude way to turn everything into integer calcs.
        // Actually do (a * (10 ^ ((dp + 1) * n))) ^ (1/n)
        // We calculate to one extra dp and round at the end
        uint one = 10 ** (1 + _dp);
        uint a0 = one ** _n * _a;

        // Initial guess: 1.0
        uint xNew = one;

        uint iter = 0;
        while (iter < _maxIts) {
            uint x = xNew;
            uint t0 = x ** (_n - 1);
            if (x * t0 > a0) {
                xNew = x - (x - a0 / t0) / _n;
            } else {
                xNew = x + (a0 / t0 - x) / _n;
            }
            ++iter;
            if(xNew == x) {
                break;
            }
        }

        // Round to nearest in the last dp.
        return (xNew + 5) / 10;
    }
}

File 5 of 5 : Strings.sol
/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant alphabet = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 1000
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"rarity","type":"uint256"},{"internalType":"bool","name":"isAlpha","type":"bool"},{"internalType":"bool","name":"offchainImage","type":"bool"}],"name":"getClassString","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"cssClass","type":"string"}],"name":"getClassStyle","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"imageBaseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"imageExtension","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxRadius","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"imageBaseURI_","type":"string"},{"internalType":"string","name":"imageExtension_","type":"string"}],"name":"setImageBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxRadius_","type":"uint256"}],"name":"setMaxRadius","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name_","type":"string"}],"name":"setName","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"rarity","type":"uint256"},{"internalType":"uint256","name":"tokenMass","type":"uint256"},{"internalType":"uint256","name":"alphaMass","type":"uint256"},{"internalType":"bool","name":"isAlpha","type":"bool"},{"internalType":"uint256","name":"mergeCount","type":"uint256"}],"name":"tokenMetadata","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"cssClass","type":"string"},{"internalType":"string","name":"cssStyle","type":"string"}],"name":"updateClassStyle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string[]","name":"imageParts_","type":"string[]"}],"name":"updateImageParts","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b50600080546001600160a01b03191633179055604080518082019091526001808252606d60f81b60209092019182526200004c9181620008c2565b506040805160208101918290526000908190526200006d91600291620008c2565b506040805160208101918290526000908190526200008e91600391620008c2565b506103e860045560058054600181018255600091825260408051608081019091526051808252600080516020620035d683398151915290920192916200364560208301398051620000e7939250602090910190620008c2565b5060058054600181018255600091909152604080518082019091526007808252661e39ba3cb6329f60c91b60209092019182526200013892600080516020620035d6833981519152019190620008c2565b50600580546001810182556000919091526040805180820190915260138082527f2e6d312023637b66696c6c3a20236666663b7d0000000000000000000000000060209092019182526200019f92600080516020620035d6833981519152019190620008c2565b50600580546001810182556000919091526040805180820190915260138082527f2e6d312023727b66696c6c3a20233030303b7d0000000000000000000000000060209092019182526200020692600080516020620035d6833981519152019190620008c2565b50600580546001810182556000919091526040805180820190915260138082527f2e6d322023637b66696c6c3a20236663333b7d0000000000000000000000000060209092019182526200026d92600080516020620035d6833981519152019190620008c2565b50600580546001810182556000919091526040805180820190915260138082527f2e6d322023727b66696c6c3a20233030303b7d000000000000000000000000006020909201918252620002d492600080516020620035d6833981519152019190620008c2565b50600580546001810182556000919091526040805180820190915260138082527f2e6d332023637b66696c6c3a20236666663b7d0000000000000000000000000060209092019182526200033b92600080516020620035d6833981519152019190620008c2565b50600580546001810182556000919091526040805180820190915260138082527f2e6d332023727b66696c6c3a20233333663b7d000000000000000000000000006020909201918252620003a292600080516020620035d6833981519152019190620008c2565b50600580546001810182556000919091526040805180820190915260138082527f2e6d342023637b66696c6c3a20236666663b7d0000000000000000000000000060209092019182526200040992600080516020620035d6833981519152019190620008c2565b50600580546001810182556000919091526040805180820190915260138082527f2e6d342023727b66696c6c3a20236633333b7d0000000000000000000000000060209092019182526200047092600080516020620035d6833981519152019190620008c2565b506005805460018101825560009190915260408051808201909152601d8082527f2e612023637b66696c6c3a20233030302021696d706f7274616e743b7d0000006020909201918252620004d792600080516020620035d6833981519152019190620008c2565b506005805460018101825560009190915260408051808201909152601d8082527f2e612023727b66696c6c3a20236666662021696d706f7274616e743b7d00000060209092019182526200053e92600080516020620035d6833981519152019190620008c2565b5060408051808201909152600d81526c1e21a620a9a9afa9aa2ca6229f60991b602082019081526005805460018101825560009190915291516200059592600080516020620035d6833981519152019190620008c2565b5060058054600181018255600091909152604080518082019091526008808252671e17b9ba3cb6329f60c11b6020909201918252620005e792600080516020620035d6833981519152019190620008c2565b506005805460018101825560009190915260408051808201909152600a808252693c6720636c6173733d2760b01b60209092019182526200063b92600080516020620035d6833981519152019190620008c2565b506040805180820190915260078152661e21a620a9a99f60c91b602082019081526005805460018101825560009190915291516200068c92600080516020620035d6833981519152019190620008c2565b506005805460018101825560009190915260408051808201909152600280825261139f60f11b6020909201918252620006d892600080516020620035d6833981519152019190620008c2565b5060058054600181018255600091825260408051606081019091526029808252600080516020620035d68339815191529092019291620035f6602083013980516200072b939250602090910190620008c2565b5060058054600181018255600091825260408051606081019091526026808252600080516020620035d683398151915290920192916200361f602083013980516200077e939250602090910190620008c2565b506040805180820190915260088152671e2920a224aaa99f60c11b60208201908152600580546001810182556000919091529151620007d092600080516020620035d6833981519152019190620008c2565b50600580546001810182556000919091526040805180820190915260038082526213979f60e91b60209092019182526200081d92600080516020620035d6833981519152019190620008c2565b5060058054600181018255600091909152604080518082019091526004808252631e17b39f60e11b60209092019182526200086b92600080516020620035d6833981519152019190620008c2565b5060058054600181018255600091909152604080518082019091526006808252651e17b9bb339f60d11b6020909201918252620008bb92600080516020620035d6833981519152019190620008c2565b50620009a5565b828054620008d09062000968565b90600052602060002090601f016020900481019282620008f457600085556200093f565b82601f106200090f57805160ff19168380011785556200093f565b828001600101855582156200093f579182015b828111156200093f57825182559160200191906001019062000922565b506200094d92915062000951565b5090565b5b808211156200094d576000815560010162000952565b600181811c908216806200097d57607f821691505b602082108114156200099f57634e487b7160e01b600052602260045260246000fd5b50919050565b612c2180620009b56000396000f3fe608060405234801561001057600080fd5b50600436106100df5760003560e01c8063a905221e1161008c578063c47f002711610066578063c47f0027146101b6578063de1c720d146101c9578063e34069af146101dc578063e8c01ad0146101ef57600080fd5b8063a905221e14610188578063a9ef3aeb1461019b578063c184f392146101a357600080fd5b806377645839116100bd578063776458391461012a57806383c3ef05146101325780638da5cb5b1461014357600080fd5b806306fdde03146100e457806322433e9014610102578063592fa5cc14610117575b600080fd5b6100ec610202565b6040516100f991906128f2565b60405180910390f35b610115610110366004612330565b610294565b005b6100ec610125366004612430565b6102bc565b6100ec610324565b6004546040519081526020016100f9565b6000546101639073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100f9565b6100ec61019636600461239c565b610333565b6100ec6103e3565b6101156101b1366004612330565b6103f2565b6101156101c43660046122ee565b610428565b6100ec6101d73660046123ea565b610441565b6101156101ea366004612226565b610458565b6101156101fd3660046123d1565b610477565b60606001805461021190612ad3565b80601f016020809104026020016040519081016040528092919081815260200182805461023d90612ad3565b801561028a5780601f1061025f5761010080835404028352916020019161028a565b820191906000526020600020905b81548152906001019060200180831161026d57829003601f168201915b5050505050905090565b61029c610484565b6102a860028585611f98565b506102b560038383611f98565b5050505050565b606060006102f56102d189898989898961050b565b6040516020016102e1919061251a565b604051602081830303815290604052610679565b9050806040516020016103089190612875565b6040516020818303038152906040529150509695505050505050565b60606002805461021190612ad3565b6060600682604051610345919061251a565b9081526020016040518091039020805461035e90612ad3565b80601f016020809104026020016040519081016040528092919081815260200182805461038a90612ad3565b80156103d75780601f106103ac576101008083540402835291602001916103d7565b820191906000526020600020905b8154815290600101906020018083116103ba57829003601f168201915b50505050509050919050565b60606003805461021190612ad3565b6103fa610484565b81816006868660405161040e929190612583565b9081526040519081900360200190206102b5929091611f98565b610430610484565b61043c60018383611f98565b505050565b606061044f85858585610816565b95945050505050565b610460610484565b805161047390600590602084019061201c565b5050565b61047f610484565b600455565b60005473ffffffffffffffffffffffffffffffffffffffff163314610509576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f596f7520617265206e6f7420746865206f776e65720000000000000000000000604482015260640160405180910390fd5b565b606060006002805461051c90612ad3565b15905061058e5761052b610324565b6105388989876001610816565b61055f61055761054b8a8a6004546109a4565b600f0b600f0b60401d90565b60070b610a12565b6105676103e3565b60405160200161057a9493929190612593565b60405160208183030381529060405261059b565b61059b8888888888610b30565b905060006040518060c001604052806000600280546105b990612ad3565b905011151581526020016105cb610202565b6105d48a610a12565b6105dd8d610a12565b6040516020016105ef9392919061263b565b604051602081830303815290604052815260200161060c89610a12565b81526020016040518060400160405280600381526020017f50616b0000000000000000000000000000000000000000000000000000000000815250815260200183815260200161065f8b8b8b898b610d93565b9052905061066c81611215565b9998505050505050505050565b606081516000141561069957505060408051602081019091526000815290565b6000604051806060016040528060408152602001612bac60409139905060006003845160026106c89190612956565b6106d2919061296e565b6106dd906004612a6d565b905060006106ec826020612956565b67ffffffffffffffff81111561070457610704612b95565b6040519080825280601f01601f19166020018201604052801561072e576020820181803683370190505b509050818152600183018586518101602084015b8183101561079a576003830192508251603f8160121c168501518253600182019150603f81600c1c168501518253600182019150603f8160061c168501518253600182019150603f8116850151825350600101610742565b6003895106600181146107b457600281146107e057610808565b7f3d3d000000000000000000000000000000000000000000000000000000000000600119830152610808565b7f3d000000000000000000000000000000000000000000000000000000000000006000198301525b509398975050505050505050565b60608080610823866114f0565b604051602001610834929190612536565b604051602081830303815290604052905083156108cb57808361087057604051806040016040528060018152602001600160fd1b81525061088b565b604051806040016040528060018152602001605f60f81b8152505b60405160200161089b91906125fa565b60408051601f19818403018152908290526108b99291602001612536565b60405160208183030381529060405290505b6000600a6108da606489612b29565b6108e4919061296e565b905060006108f3600a89612b29565b905060008161090384600a612a6d565b61090d9190612956565b9050838661093457604051806040016040528060018152602001600160fd1b81525061094f565b604051806040016040528060018152602001605f60f81b8152505b61095883611521565b604051602001610969929190612536565b60408051601f19818403018152908290526109879291602001612536565b60408051808303601f190181529190529998505050505050505050565b6000806109b08561153c565b905060006109bd8561153c565b905060006109cb8383611580565b905060006109e16109db876115f4565b83611612565b90506109f381600f0b600f0b60401d90565b60070b610a0757610a0460016115f4565b90505b979650505050505050565b606081610a365750506040805180820190915260018152600360fc1b602082015290565b8160005b8115610a605780610a4a81612b0e565b9150610a599050600a8361296e565b9150610a3a565b60008167ffffffffffffffff811115610a7b57610a7b612b95565b6040519080825280601f01601f191660200182016040528015610aa5576020820181803683370190505b5090505b8415610b2857610aba600183612a8c565b9150610ac7600a86612b29565b610ad2906030612956565b60f81b818381518110610ae757610ae7612b7f565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350610b21600a8661296e565b9450610aa9565b949350505050565b60608060005b600554811015610d8857610b9b60058281548110610b5657610b56612b7f565b906000526020600020016040518060400160405280600881526020017f3c5241444955533e00000000000000000000000000000000000000000000000081525061165a565b15610bdd5781610bb6610bb188886004546109a4565b6116b3565b604051602001610bc7929190612536565b6040516020818303038152906040529150610d76565b610c3860058281548110610bf357610bf3612b7f565b906000526020600020016040518060400160405280600781526020017f3c434c4153533e0000000000000000000000000000000000000000000000000081525061165a565b15610c4b5781610bb68989876000610816565b610ca660058281548110610c6157610c61612b7f565b906000526020600020016040518060400160405280600d81526020017f3c434c4153535f5354594c453e0000000000000000000000000000000000000081525061165a565b15610d35576000600a610cba60648b612b29565b610cc4919061296e565b90506000610cd3600a8b612b29565b9050600081610ce384600a612a6d565b610ced9190612956565b90506000610cfd61019683611521565b805190915015610d2c578581604051602001610d1a929190612536565b60405160208183030381529060405295505b50505050610d76565b8160058281548110610d4957610d49612b7f565b90600052602060002001604051602001610d64929190612565565b60405160208183030381529060405291505b80610d8081612b0e565b915050610b36565b509695505050505050565b60606000600a610da4606489612b29565b610dae919061296e565b90506000610dbd600a89612b29565b9050600081610dcd84600a612a6d565b610dd79190612956565b60408051600580825260c0820190925291925060009190816020015b6040805160c081018252600080825260208083018290529282015260608082018190526080820181905260a08201528252600019909201910181610df3579050509050610ef6600060016000604051806020016040528060008152506040518060400160405280600481526020017f4d61737300000000000000000000000000000000000000000000000000000000815250610e8e8e610a12565b6040805160c0810182526000808252602082018190529181019190915260608082018190526080820181905260a0820152506040805160c08101825296151587529415156020870152921515938501939093526060840152608083019190915260a082015290565b81600081518110610f0957610f09612b7f565b6020026020010181905250611088600060016000604051806020016040528060008152506040518060400160405280600581526020017f416c7068610000000000000000000000000000000000000000000000000000008152508b610fea57604051806040016040528060018152602001600360fc1b8152506040805160c0810182526000808252602082018190529181019190915260608082018190526080820181905260a0820152506040805160c08101825296151587529415156020870152921515938501939093526060840152608083019190915260a082015290565b6040518060400160405280600181526020017f31000000000000000000000000000000000000000000000000000000000000008152506040805160c0810182526000808252602082018190529181019190915260608082018190526080820181905260a0820152506040805160c08101825296151587529415156020870152921515938501939093526060840152608083019190915260a082015290565b8160018151811061109b5761109b612b7f565b60200260200101819052506110fe600060016000604051806020016040528060008152506040518060400160405280600481526020017f5469657200000000000000000000000000000000000000000000000000000000815250610e8e8f610a12565b8160028151811061111157611111612b7f565b6020026020010181905250611174600060016000604051806020016040528060008152506040518060400160405280600581526020017f436c617373000000000000000000000000000000000000000000000000000000815250610e8e88610a12565b8160038151811061118757611187612b7f565b60200260200101819052506111ea600060016000604051806020016040528060008152506040518060400160405280600681526020017f4d65726765730000000000000000000000000000000000000000000000000000815250610e8e8d610a12565b816004815181106111fd576111fd612b7f565b60209081029190910101529998505050505050505050565b60608080611221611716565b604051602001611232929190612536565b6040516020818303038152906040529050806112896040518060400160405280600481526020017f6e616d65000000000000000000000000000000000000000000000000000000008152508560200151600161175a565b60405160200161129a929190612536565b60408051601f198184030181528282018252600b83527f6465736372697074696f6e00000000000000000000000000000000000000000060208401529085015190925082916112ea91600161175a565b6040516020016112fb929190612536565b60408051601f19818403018152828201909152600a82527f637265617465645f62790000000000000000000000000000000000000000000060208301526060850151909250829161134d91600161175a565b60405160200161135e929190612536565b60405160208183030381529060405290508260000151156113e657806113bf6040518060400160405280600581526020017f696d6167650000000000000000000000000000000000000000000000000000008152508560800151600161175a565b6040516020016113d0929190612536565b604051602081830303815290604052905061144f565b8061142c6040518060400160405280600a81526020017f696d6167655f64617461000000000000000000000000000000000000000000008152508560800151600161175a565b60405160200161143d929190612536565b60405160208183030381529060405290505b8061149d6040518060400160405280600a81526020017f61747472696275746573000000000000000000000000000000000000000000008152506114968660a001516117bd565b6000611877565b6040516020016114ae929190612536565b6040516020818303038152906040529050806114c86118c2565b6040516020016114d9929190612536565b60408051601f198184030181529190529392505050565b60606114fb82610a12565b60405160200161150b91906128ba565b6040516020818303038152906040529050919050565b606061152c82610a12565b60405160200161150b919061273d565b600080611553640e7193ba8164174876e8006118f6565b905060006115726115698560036006602061193f565b620f42406118f6565b9050600061044f8284611612565b600081600f0b6000141561159357600080fd5b600082600f0b604085600f0b901b816115ae576115ae612b69565b0590506f7fffffffffffffffffffffffffffffff1981128015906115e257506f7fffffffffffffffffffffffffffffff8113155b6115eb57600080fd5b90505b92915050565b6000677fffffffffffffff82111561160b57600080fd5b5060401b90565b6000600f83810b9083900b0260401d6f7fffffffffffffffffffffffffffffff1981128015906115e257506f7fffffffffffffffffffffffffffffff8113156115eb57600080fd5b60008160405160200161166d919061251a565b6040516020818303038152906040528051906020012083604051602001611694919061272a565b6040516020818303038152906040528051906020012014905092915050565b606060006116d067ffffffffffffffff8416600f0b612710611a50565b90506116e561055784600f0b600f0b60401d90565b6116ee82611ad2565b6040516020016116ff9291906126d2565b604051602081830303815290604052915050919050565b6040517f7b0000000000000000000000000000000000000000000000000000000000000060208201526060906021015b604051602081830303815290604052905090565b60608383836117785760405180602001604052806000815250611793565b604051806040016040528060018152602001600b60fa1b8152505b6040516020016117a593929190612782565b60405160208183030381529060405290509392505050565b606080806117c9611b98565b6040516020016117da929190612536565b604051602081830303815290604052905060005b835181101561186d57600084828151811061180b5761180b612b7f565b602002602001015190508261183761182283611bcc565b600188516118309190612a8c565b8510611d76565b604051602001611848929190612536565b604051602081830303815290604052925050808061186590612b0e565b9150506117ee565b50806114c8611dd6565b606083838361189557604051806020016040528060008152506118b0565b604051806040016040528060018152602001600b60fa1b8152505b6040516020016117a593929190612806565b6040517f7d000000000000000000000000000000000000000000000000000000000000006020820152606090602101611746565b60008161190257600080fd5b600061190e8484611e0a565b90506f7fffffffffffffffffffffffffffffff6fffffffffffffffffffffffffffffffff821611156115eb57600080fd5b60006001841161195157611951612b3d565b600061195e846001612956565b61196990600a6129c5565b905060008661197887846129c5565b6119829190612a6d565b90508160005b85811015611a395781600061199e60018b612a8c565b6119a890836129c5565b9050846119b58284612a6d565b11156119eb57896119c6828761296e565b6119d09084612a8c565b6119da919061296e565b6119e49083612a8c565b9350611a18565b89826119f7838861296e565b611a019190612a8c565b611a0b919061296e565b611a159083612956565b93505b611a2183612b0e565b925081841415611a32575050611a39565b5050611988565b600a611a46836005612956565b61066c919061296e565b600081611a5f575060006115ee565b600083600f0b1215611a7057600080fd5b600f83900b6fffffffffffffffffffffffffffffffff8316810260401c90608084901c0277ffffffffffffffffffffffffffffffffffffffffffffffff811115611ab957600080fd5b60401b8119811115611aca57600080fd5b019392505050565b6040805160048082528183019092526060916000919060208201818036833701905050905060005b6004811015611b8657611b0e600a85612b29565b611b19906030612956565b60f81b82611b28836003612a8c565b81518110611b3857611b38612b7f565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350611b72600a8561296e565b935080611b7e81612b0e565b915050611afa565b50806040516020016116ff919061251a565b6040517f5b000000000000000000000000000000000000000000000000000000000000006020820152606090602101611746565b60608080611bd8611716565b604051602001611be9929190612536565b6040516020818303038152906040529050826000015115611c6d5780611c4a6040518060400160405280600c81526020017f646973706c61795f7479706500000000000000000000000000000000000000008152508560600151600161175a565b604051602001611c5b929190612536565b60405160208183030381529060405290505b826020015115611ce05780611cbd6040518060400160405280600a81526020017f74726169745f74797065000000000000000000000000000000000000000000008152508560800151600161175a565b604051602001611cce929190612536565b60405160208183030381529060405290505b826040015115611d3f5780611d186040518060400160405280600581526020016476616c756560d81b8152508560a00151600061175a565b604051602001611d29929190612536565b6040516020818303038152906040529050611d6d565b8061149d6040518060400160405280600581526020016476616c756560d81b8152508560a001516000611877565b806114c86118c2565b60608282611d935760405180602001604052806000815250611dae565b604051806040016040528060018152602001600b60fa1b8152505b604051602001611dbf929190612536565b604051602081830303815290604052905092915050565b6040517f5d000000000000000000000000000000000000000000000000000000000000006020820152606090602101611746565b600081611e1657600080fd5b600077ffffffffffffffffffffffffffffffffffffffffffffffff8411611e525782604085901b81611e4a57611e4a612b69565b049050611f7b565b60c084811c6401000000008110611e6b576020918201911c5b620100008110611e7d576010918201911c5b6101008110611e8e576008918201911c5b60108110611e9e576004918201911c5b60048110611eae576002918201911c5b60028110611ebd576001820191505b60bf820360018603901c6001018260ff0387901b81611ede57611ede612b69565b0492506fffffffffffffffffffffffffffffffff831115611efe57600080fd5b608085901c83026fffffffffffffffffffffffffffffffff8616840260c088901c604089901b82811015611f33576001820391505b608084901b92900382811015611f4a576001820391505b829003608084901c8214611f6057611f60612b3d565b888181611f6f57611f6f612b69565b04870196505050505050505b6fffffffffffffffffffffffffffffffff8111156115eb57600080fd5b828054611fa490612ad3565b90600052602060002090601f016020900481019282611fc6576000855561200c565b82601f10611fdf5782800160ff1982351617855561200c565b8280016001018555821561200c579182015b8281111561200c578235825591602001919060010190611ff1565b50612018929150612075565b5090565b828054828255906000526020600020908101928215612069579160200282015b82811115612069578251805161205991849160209091019061208a565b509160200191906001019061203c565b506120189291506120fe565b5b808211156120185760008155600101612076565b82805461209690612ad3565b90600052602060002090601f0160209004810192826120b8576000855561200c565b82601f106120d157805160ff191683800117855561200c565b8280016001018555821561200c579182015b8281111561200c5782518255916020019190600101906120e3565b80821115612018576000612112828261211b565b506001016120fe565b50805461212790612ad3565b6000825580601f10612137575050565b601f0160209004906000526020600020908101906121559190612075565b50565b8035801515811461216857600080fd5b919050565b60008083601f84011261217f57600080fd5b50813567ffffffffffffffff81111561219757600080fd5b6020830191508360208285010111156121af57600080fd5b9250929050565b600082601f8301126121c757600080fd5b813567ffffffffffffffff8111156121e1576121e1612b95565b6121f4601f8201601f1916602001612925565b81815284602083860101111561220957600080fd5b816020850160208301376000918101602001919091529392505050565b6000602080838503121561223957600080fd5b823567ffffffffffffffff8082111561225157600080fd5b818501915085601f83011261226557600080fd5b81358181111561227757612277612b95565b8060051b612286858201612925565b8281528581019085870183870188018b10156122a157600080fd5b60009350835b858110156122de578135878111156122bd578586fd5b6122cb8d8b838c01016121b6565b85525092880192908801906001016122a7565b50909a9950505050505050505050565b6000806020838503121561230157600080fd5b823567ffffffffffffffff81111561231857600080fd5b6123248582860161216d565b90969095509350505050565b6000806000806040858703121561234657600080fd5b843567ffffffffffffffff8082111561235e57600080fd5b61236a8883890161216d565b9096509450602087013591508082111561238357600080fd5b506123908782880161216d565b95989497509550505050565b6000602082840312156123ae57600080fd5b813567ffffffffffffffff8111156123c557600080fd5b610b28848285016121b6565b6000602082840312156123e357600080fd5b5035919050565b6000806000806080858703121561240057600080fd5b843593506020850135925061241760408601612158565b915061242560608601612158565b905092959194509250565b60008060008060008060c0878903121561244957600080fd5b8635955060208701359450604087013593506060870135925061246e60808801612158565b915060a087013590509295509295509295565b8054600090600181811c908083168061249b57607f831692505b60208084108214156124bd57634e487b7160e01b600052602260045260246000fd5b8180156124d157600181146124e25761250e565b60ff1986168952848901965061250e565b876000528160002060005b868110156125065781548b8201529085019083016124ed565b505084890196505b50505050505092915050565b6000825161252c818460208701612aa3565b9190910192915050565b60008351612548818460208801612aa3565b83519083019061255c818360208801612aa3565b01949350505050565b60008351612577818460208801612aa3565b61044f81840185612481565b8183823760009101908152919050565b600085516125a5818460208a01612aa3565b8551908301906125b9818360208a01612aa3565b605f60f81b910190815284516125d6816001840160208901612aa3565b84519101906125ec816001840160208801612aa3565b016001019695505050505050565b6000825161260c818460208701612aa3565b7f6100000000000000000000000000000000000000000000000000000000000000920191825250600101919050565b6000845161264d818460208901612aa3565b7f28000000000000000000000000000000000000000000000000000000000000009083019081528451612687816001840160208901612aa3565b7f29202300000000000000000000000000000000000000000000000000000000006001929091019182015283516126c5816004840160208801612aa3565b0160040195945050505050565b600083516126e4818460208801612aa3565b7f2e00000000000000000000000000000000000000000000000000000000000000908301908152835161271e816001840160208801612aa3565b01600101949350505050565b60006127368284612481565b9392505050565b7f6300000000000000000000000000000000000000000000000000000000000000815260008251612775816001850160208701612aa3565b9190910160010192915050565b6000601160f91b808352855161279f816001860160208a01612aa3565b7f223a20220000000000000000000000000000000000000000000000000000000060019185019182015285516127dc816005840160208a01612aa3565b0160058101919091528351906127f9826006830160208801612aa3565b0160060195945050505050565b601160f91b815260008451612822816001850160208901612aa3565b7f223a200000000000000000000000000000000000000000000000000000000000600191840191820152845161285f816004840160208901612aa3565b84519101906126c5816004840160208801612aa3565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c0000008152600082516128ad81601d850160208701612aa3565b91909101601d0192915050565b7f6d00000000000000000000000000000000000000000000000000000000000000815260008251612775816001850160208701612aa3565b6020815260008251806020840152612911816040850160208701612aa3565b601f01601f19169190910160400192915050565b604051601f8201601f1916810167ffffffffffffffff8111828210171561294e5761294e612b95565b604052919050565b6000821982111561296957612969612b53565b500190565b60008261297d5761297d612b69565b500490565b600181815b808511156129bd5781600019048211156129a3576129a3612b53565b808516156129b057918102915b93841c9390800290612987565b509250929050565b600061273683836000826129db575060016115ee565b816129e8575060006115ee565b81600181146129fe5760028114612a0857612a24565b60019150506115ee565b60ff841115612a1957612a19612b53565b50506001821b6115ee565b5060208310610133831016604e8410600b8410161715612a47575081810a6115ee565b612a518383612982565b8060001904821115612a6557612a65612b53565b029392505050565b6000816000190483118215151615612a8757612a87612b53565b500290565b600082821015612a9e57612a9e612b53565b500390565b60005b83811015612abe578181015183820152602001612aa6565b83811115612acd576000848401525b50505050565b600181811c90821680612ae757607f821691505b60208210811415612b0857634e487b7160e01b600052602260045260246000fd5b50919050565b6000600019821415612b2257612b22612b53565b5060010190565b600082612b3857612b38612b69565b500690565b634e487b7160e01b600052600160045260246000fd5b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fdfe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa2646970667358221220c9649e6d47b84a7fd8c986e39c5b0f823a4fefdb5a87a8f52d09e3f2974e2a0e64736f6c63430008060033036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db03c726563742069643d2772272077696474683d273230303027206865696768743d2732303030272f3e3c636972636c652069643d2763272063783d2731303030272063793d27313030302720723d273c73766720786d6c6e733d27687474703a2f2f7777772e77332e6f72672f323030302f737667272076657273696f6e3d27312e31272077696474683d273230303027206865696768743d2732303030273e

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100df5760003560e01c8063a905221e1161008c578063c47f002711610066578063c47f0027146101b6578063de1c720d146101c9578063e34069af146101dc578063e8c01ad0146101ef57600080fd5b8063a905221e14610188578063a9ef3aeb1461019b578063c184f392146101a357600080fd5b806377645839116100bd578063776458391461012a57806383c3ef05146101325780638da5cb5b1461014357600080fd5b806306fdde03146100e457806322433e9014610102578063592fa5cc14610117575b600080fd5b6100ec610202565b6040516100f991906128f2565b60405180910390f35b610115610110366004612330565b610294565b005b6100ec610125366004612430565b6102bc565b6100ec610324565b6004546040519081526020016100f9565b6000546101639073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100f9565b6100ec61019636600461239c565b610333565b6100ec6103e3565b6101156101b1366004612330565b6103f2565b6101156101c43660046122ee565b610428565b6100ec6101d73660046123ea565b610441565b6101156101ea366004612226565b610458565b6101156101fd3660046123d1565b610477565b60606001805461021190612ad3565b80601f016020809104026020016040519081016040528092919081815260200182805461023d90612ad3565b801561028a5780601f1061025f5761010080835404028352916020019161028a565b820191906000526020600020905b81548152906001019060200180831161026d57829003601f168201915b5050505050905090565b61029c610484565b6102a860028585611f98565b506102b560038383611f98565b5050505050565b606060006102f56102d189898989898961050b565b6040516020016102e1919061251a565b604051602081830303815290604052610679565b9050806040516020016103089190612875565b6040516020818303038152906040529150509695505050505050565b60606002805461021190612ad3565b6060600682604051610345919061251a565b9081526020016040518091039020805461035e90612ad3565b80601f016020809104026020016040519081016040528092919081815260200182805461038a90612ad3565b80156103d75780601f106103ac576101008083540402835291602001916103d7565b820191906000526020600020905b8154815290600101906020018083116103ba57829003601f168201915b50505050509050919050565b60606003805461021190612ad3565b6103fa610484565b81816006868660405161040e929190612583565b9081526040519081900360200190206102b5929091611f98565b610430610484565b61043c60018383611f98565b505050565b606061044f85858585610816565b95945050505050565b610460610484565b805161047390600590602084019061201c565b5050565b61047f610484565b600455565b60005473ffffffffffffffffffffffffffffffffffffffff163314610509576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f596f7520617265206e6f7420746865206f776e65720000000000000000000000604482015260640160405180910390fd5b565b606060006002805461051c90612ad3565b15905061058e5761052b610324565b6105388989876001610816565b61055f61055761054b8a8a6004546109a4565b600f0b600f0b60401d90565b60070b610a12565b6105676103e3565b60405160200161057a9493929190612593565b60405160208183030381529060405261059b565b61059b8888888888610b30565b905060006040518060c001604052806000600280546105b990612ad3565b905011151581526020016105cb610202565b6105d48a610a12565b6105dd8d610a12565b6040516020016105ef9392919061263b565b604051602081830303815290604052815260200161060c89610a12565b81526020016040518060400160405280600381526020017f50616b0000000000000000000000000000000000000000000000000000000000815250815260200183815260200161065f8b8b8b898b610d93565b9052905061066c81611215565b9998505050505050505050565b606081516000141561069957505060408051602081019091526000815290565b6000604051806060016040528060408152602001612bac60409139905060006003845160026106c89190612956565b6106d2919061296e565b6106dd906004612a6d565b905060006106ec826020612956565b67ffffffffffffffff81111561070457610704612b95565b6040519080825280601f01601f19166020018201604052801561072e576020820181803683370190505b509050818152600183018586518101602084015b8183101561079a576003830192508251603f8160121c168501518253600182019150603f81600c1c168501518253600182019150603f8160061c168501518253600182019150603f8116850151825350600101610742565b6003895106600181146107b457600281146107e057610808565b7f3d3d000000000000000000000000000000000000000000000000000000000000600119830152610808565b7f3d000000000000000000000000000000000000000000000000000000000000006000198301525b509398975050505050505050565b60608080610823866114f0565b604051602001610834929190612536565b604051602081830303815290604052905083156108cb57808361087057604051806040016040528060018152602001600160fd1b81525061088b565b604051806040016040528060018152602001605f60f81b8152505b60405160200161089b91906125fa565b60408051601f19818403018152908290526108b99291602001612536565b60405160208183030381529060405290505b6000600a6108da606489612b29565b6108e4919061296e565b905060006108f3600a89612b29565b905060008161090384600a612a6d565b61090d9190612956565b9050838661093457604051806040016040528060018152602001600160fd1b81525061094f565b604051806040016040528060018152602001605f60f81b8152505b61095883611521565b604051602001610969929190612536565b60408051601f19818403018152908290526109879291602001612536565b60408051808303601f190181529190529998505050505050505050565b6000806109b08561153c565b905060006109bd8561153c565b905060006109cb8383611580565b905060006109e16109db876115f4565b83611612565b90506109f381600f0b600f0b60401d90565b60070b610a0757610a0460016115f4565b90505b979650505050505050565b606081610a365750506040805180820190915260018152600360fc1b602082015290565b8160005b8115610a605780610a4a81612b0e565b9150610a599050600a8361296e565b9150610a3a565b60008167ffffffffffffffff811115610a7b57610a7b612b95565b6040519080825280601f01601f191660200182016040528015610aa5576020820181803683370190505b5090505b8415610b2857610aba600183612a8c565b9150610ac7600a86612b29565b610ad2906030612956565b60f81b818381518110610ae757610ae7612b7f565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350610b21600a8661296e565b9450610aa9565b949350505050565b60608060005b600554811015610d8857610b9b60058281548110610b5657610b56612b7f565b906000526020600020016040518060400160405280600881526020017f3c5241444955533e00000000000000000000000000000000000000000000000081525061165a565b15610bdd5781610bb6610bb188886004546109a4565b6116b3565b604051602001610bc7929190612536565b6040516020818303038152906040529150610d76565b610c3860058281548110610bf357610bf3612b7f565b906000526020600020016040518060400160405280600781526020017f3c434c4153533e0000000000000000000000000000000000000000000000000081525061165a565b15610c4b5781610bb68989876000610816565b610ca660058281548110610c6157610c61612b7f565b906000526020600020016040518060400160405280600d81526020017f3c434c4153535f5354594c453e0000000000000000000000000000000000000081525061165a565b15610d35576000600a610cba60648b612b29565b610cc4919061296e565b90506000610cd3600a8b612b29565b9050600081610ce384600a612a6d565b610ced9190612956565b90506000610cfd61019683611521565b805190915015610d2c578581604051602001610d1a929190612536565b60405160208183030381529060405295505b50505050610d76565b8160058281548110610d4957610d49612b7f565b90600052602060002001604051602001610d64929190612565565b60405160208183030381529060405291505b80610d8081612b0e565b915050610b36565b509695505050505050565b60606000600a610da4606489612b29565b610dae919061296e565b90506000610dbd600a89612b29565b9050600081610dcd84600a612a6d565b610dd79190612956565b60408051600580825260c0820190925291925060009190816020015b6040805160c081018252600080825260208083018290529282015260608082018190526080820181905260a08201528252600019909201910181610df3579050509050610ef6600060016000604051806020016040528060008152506040518060400160405280600481526020017f4d61737300000000000000000000000000000000000000000000000000000000815250610e8e8e610a12565b6040805160c0810182526000808252602082018190529181019190915260608082018190526080820181905260a0820152506040805160c08101825296151587529415156020870152921515938501939093526060840152608083019190915260a082015290565b81600081518110610f0957610f09612b7f565b6020026020010181905250611088600060016000604051806020016040528060008152506040518060400160405280600581526020017f416c7068610000000000000000000000000000000000000000000000000000008152508b610fea57604051806040016040528060018152602001600360fc1b8152506040805160c0810182526000808252602082018190529181019190915260608082018190526080820181905260a0820152506040805160c08101825296151587529415156020870152921515938501939093526060840152608083019190915260a082015290565b6040518060400160405280600181526020017f31000000000000000000000000000000000000000000000000000000000000008152506040805160c0810182526000808252602082018190529181019190915260608082018190526080820181905260a0820152506040805160c08101825296151587529415156020870152921515938501939093526060840152608083019190915260a082015290565b8160018151811061109b5761109b612b7f565b60200260200101819052506110fe600060016000604051806020016040528060008152506040518060400160405280600481526020017f5469657200000000000000000000000000000000000000000000000000000000815250610e8e8f610a12565b8160028151811061111157611111612b7f565b6020026020010181905250611174600060016000604051806020016040528060008152506040518060400160405280600581526020017f436c617373000000000000000000000000000000000000000000000000000000815250610e8e88610a12565b8160038151811061118757611187612b7f565b60200260200101819052506111ea600060016000604051806020016040528060008152506040518060400160405280600681526020017f4d65726765730000000000000000000000000000000000000000000000000000815250610e8e8d610a12565b816004815181106111fd576111fd612b7f565b60209081029190910101529998505050505050505050565b60608080611221611716565b604051602001611232929190612536565b6040516020818303038152906040529050806112896040518060400160405280600481526020017f6e616d65000000000000000000000000000000000000000000000000000000008152508560200151600161175a565b60405160200161129a929190612536565b60408051601f198184030181528282018252600b83527f6465736372697074696f6e00000000000000000000000000000000000000000060208401529085015190925082916112ea91600161175a565b6040516020016112fb929190612536565b60408051601f19818403018152828201909152600a82527f637265617465645f62790000000000000000000000000000000000000000000060208301526060850151909250829161134d91600161175a565b60405160200161135e929190612536565b60405160208183030381529060405290508260000151156113e657806113bf6040518060400160405280600581526020017f696d6167650000000000000000000000000000000000000000000000000000008152508560800151600161175a565b6040516020016113d0929190612536565b604051602081830303815290604052905061144f565b8061142c6040518060400160405280600a81526020017f696d6167655f64617461000000000000000000000000000000000000000000008152508560800151600161175a565b60405160200161143d929190612536565b60405160208183030381529060405290505b8061149d6040518060400160405280600a81526020017f61747472696275746573000000000000000000000000000000000000000000008152506114968660a001516117bd565b6000611877565b6040516020016114ae929190612536565b6040516020818303038152906040529050806114c86118c2565b6040516020016114d9929190612536565b60408051601f198184030181529190529392505050565b60606114fb82610a12565b60405160200161150b91906128ba565b6040516020818303038152906040529050919050565b606061152c82610a12565b60405160200161150b919061273d565b600080611553640e7193ba8164174876e8006118f6565b905060006115726115698560036006602061193f565b620f42406118f6565b9050600061044f8284611612565b600081600f0b6000141561159357600080fd5b600082600f0b604085600f0b901b816115ae576115ae612b69565b0590506f7fffffffffffffffffffffffffffffff1981128015906115e257506f7fffffffffffffffffffffffffffffff8113155b6115eb57600080fd5b90505b92915050565b6000677fffffffffffffff82111561160b57600080fd5b5060401b90565b6000600f83810b9083900b0260401d6f7fffffffffffffffffffffffffffffff1981128015906115e257506f7fffffffffffffffffffffffffffffff8113156115eb57600080fd5b60008160405160200161166d919061251a565b6040516020818303038152906040528051906020012083604051602001611694919061272a565b6040516020818303038152906040528051906020012014905092915050565b606060006116d067ffffffffffffffff8416600f0b612710611a50565b90506116e561055784600f0b600f0b60401d90565b6116ee82611ad2565b6040516020016116ff9291906126d2565b604051602081830303815290604052915050919050565b6040517f7b0000000000000000000000000000000000000000000000000000000000000060208201526060906021015b604051602081830303815290604052905090565b60608383836117785760405180602001604052806000815250611793565b604051806040016040528060018152602001600b60fa1b8152505b6040516020016117a593929190612782565b60405160208183030381529060405290509392505050565b606080806117c9611b98565b6040516020016117da929190612536565b604051602081830303815290604052905060005b835181101561186d57600084828151811061180b5761180b612b7f565b602002602001015190508261183761182283611bcc565b600188516118309190612a8c565b8510611d76565b604051602001611848929190612536565b604051602081830303815290604052925050808061186590612b0e565b9150506117ee565b50806114c8611dd6565b606083838361189557604051806020016040528060008152506118b0565b604051806040016040528060018152602001600b60fa1b8152505b6040516020016117a593929190612806565b6040517f7d000000000000000000000000000000000000000000000000000000000000006020820152606090602101611746565b60008161190257600080fd5b600061190e8484611e0a565b90506f7fffffffffffffffffffffffffffffff6fffffffffffffffffffffffffffffffff821611156115eb57600080fd5b60006001841161195157611951612b3d565b600061195e846001612956565b61196990600a6129c5565b905060008661197887846129c5565b6119829190612a6d565b90508160005b85811015611a395781600061199e60018b612a8c565b6119a890836129c5565b9050846119b58284612a6d565b11156119eb57896119c6828761296e565b6119d09084612a8c565b6119da919061296e565b6119e49083612a8c565b9350611a18565b89826119f7838861296e565b611a019190612a8c565b611a0b919061296e565b611a159083612956565b93505b611a2183612b0e565b925081841415611a32575050611a39565b5050611988565b600a611a46836005612956565b61066c919061296e565b600081611a5f575060006115ee565b600083600f0b1215611a7057600080fd5b600f83900b6fffffffffffffffffffffffffffffffff8316810260401c90608084901c0277ffffffffffffffffffffffffffffffffffffffffffffffff811115611ab957600080fd5b60401b8119811115611aca57600080fd5b019392505050565b6040805160048082528183019092526060916000919060208201818036833701905050905060005b6004811015611b8657611b0e600a85612b29565b611b19906030612956565b60f81b82611b28836003612a8c565b81518110611b3857611b38612b7f565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350611b72600a8561296e565b935080611b7e81612b0e565b915050611afa565b50806040516020016116ff919061251a565b6040517f5b000000000000000000000000000000000000000000000000000000000000006020820152606090602101611746565b60608080611bd8611716565b604051602001611be9929190612536565b6040516020818303038152906040529050826000015115611c6d5780611c4a6040518060400160405280600c81526020017f646973706c61795f7479706500000000000000000000000000000000000000008152508560600151600161175a565b604051602001611c5b929190612536565b60405160208183030381529060405290505b826020015115611ce05780611cbd6040518060400160405280600a81526020017f74726169745f74797065000000000000000000000000000000000000000000008152508560800151600161175a565b604051602001611cce929190612536565b60405160208183030381529060405290505b826040015115611d3f5780611d186040518060400160405280600581526020016476616c756560d81b8152508560a00151600061175a565b604051602001611d29929190612536565b6040516020818303038152906040529050611d6d565b8061149d6040518060400160405280600581526020016476616c756560d81b8152508560a001516000611877565b806114c86118c2565b60608282611d935760405180602001604052806000815250611dae565b604051806040016040528060018152602001600b60fa1b8152505b604051602001611dbf929190612536565b604051602081830303815290604052905092915050565b6040517f5d000000000000000000000000000000000000000000000000000000000000006020820152606090602101611746565b600081611e1657600080fd5b600077ffffffffffffffffffffffffffffffffffffffffffffffff8411611e525782604085901b81611e4a57611e4a612b69565b049050611f7b565b60c084811c6401000000008110611e6b576020918201911c5b620100008110611e7d576010918201911c5b6101008110611e8e576008918201911c5b60108110611e9e576004918201911c5b60048110611eae576002918201911c5b60028110611ebd576001820191505b60bf820360018603901c6001018260ff0387901b81611ede57611ede612b69565b0492506fffffffffffffffffffffffffffffffff831115611efe57600080fd5b608085901c83026fffffffffffffffffffffffffffffffff8616840260c088901c604089901b82811015611f33576001820391505b608084901b92900382811015611f4a576001820391505b829003608084901c8214611f6057611f60612b3d565b888181611f6f57611f6f612b69565b04870196505050505050505b6fffffffffffffffffffffffffffffffff8111156115eb57600080fd5b828054611fa490612ad3565b90600052602060002090601f016020900481019282611fc6576000855561200c565b82601f10611fdf5782800160ff1982351617855561200c565b8280016001018555821561200c579182015b8281111561200c578235825591602001919060010190611ff1565b50612018929150612075565b5090565b828054828255906000526020600020908101928215612069579160200282015b82811115612069578251805161205991849160209091019061208a565b509160200191906001019061203c565b506120189291506120fe565b5b808211156120185760008155600101612076565b82805461209690612ad3565b90600052602060002090601f0160209004810192826120b8576000855561200c565b82601f106120d157805160ff191683800117855561200c565b8280016001018555821561200c579182015b8281111561200c5782518255916020019190600101906120e3565b80821115612018576000612112828261211b565b506001016120fe565b50805461212790612ad3565b6000825580601f10612137575050565b601f0160209004906000526020600020908101906121559190612075565b50565b8035801515811461216857600080fd5b919050565b60008083601f84011261217f57600080fd5b50813567ffffffffffffffff81111561219757600080fd5b6020830191508360208285010111156121af57600080fd5b9250929050565b600082601f8301126121c757600080fd5b813567ffffffffffffffff8111156121e1576121e1612b95565b6121f4601f8201601f1916602001612925565b81815284602083860101111561220957600080fd5b816020850160208301376000918101602001919091529392505050565b6000602080838503121561223957600080fd5b823567ffffffffffffffff8082111561225157600080fd5b818501915085601f83011261226557600080fd5b81358181111561227757612277612b95565b8060051b612286858201612925565b8281528581019085870183870188018b10156122a157600080fd5b60009350835b858110156122de578135878111156122bd578586fd5b6122cb8d8b838c01016121b6565b85525092880192908801906001016122a7565b50909a9950505050505050505050565b6000806020838503121561230157600080fd5b823567ffffffffffffffff81111561231857600080fd5b6123248582860161216d565b90969095509350505050565b6000806000806040858703121561234657600080fd5b843567ffffffffffffffff8082111561235e57600080fd5b61236a8883890161216d565b9096509450602087013591508082111561238357600080fd5b506123908782880161216d565b95989497509550505050565b6000602082840312156123ae57600080fd5b813567ffffffffffffffff8111156123c557600080fd5b610b28848285016121b6565b6000602082840312156123e357600080fd5b5035919050565b6000806000806080858703121561240057600080fd5b843593506020850135925061241760408601612158565b915061242560608601612158565b905092959194509250565b60008060008060008060c0878903121561244957600080fd5b8635955060208701359450604087013593506060870135925061246e60808801612158565b915060a087013590509295509295509295565b8054600090600181811c908083168061249b57607f831692505b60208084108214156124bd57634e487b7160e01b600052602260045260246000fd5b8180156124d157600181146124e25761250e565b60ff1986168952848901965061250e565b876000528160002060005b868110156125065781548b8201529085019083016124ed565b505084890196505b50505050505092915050565b6000825161252c818460208701612aa3565b9190910192915050565b60008351612548818460208801612aa3565b83519083019061255c818360208801612aa3565b01949350505050565b60008351612577818460208801612aa3565b61044f81840185612481565b8183823760009101908152919050565b600085516125a5818460208a01612aa3565b8551908301906125b9818360208a01612aa3565b605f60f81b910190815284516125d6816001840160208901612aa3565b84519101906125ec816001840160208801612aa3565b016001019695505050505050565b6000825161260c818460208701612aa3565b7f6100000000000000000000000000000000000000000000000000000000000000920191825250600101919050565b6000845161264d818460208901612aa3565b7f28000000000000000000000000000000000000000000000000000000000000009083019081528451612687816001840160208901612aa3565b7f29202300000000000000000000000000000000000000000000000000000000006001929091019182015283516126c5816004840160208801612aa3565b0160040195945050505050565b600083516126e4818460208801612aa3565b7f2e00000000000000000000000000000000000000000000000000000000000000908301908152835161271e816001840160208801612aa3565b01600101949350505050565b60006127368284612481565b9392505050565b7f6300000000000000000000000000000000000000000000000000000000000000815260008251612775816001850160208701612aa3565b9190910160010192915050565b6000601160f91b808352855161279f816001860160208a01612aa3565b7f223a20220000000000000000000000000000000000000000000000000000000060019185019182015285516127dc816005840160208a01612aa3565b0160058101919091528351906127f9826006830160208801612aa3565b0160060195945050505050565b601160f91b815260008451612822816001850160208901612aa3565b7f223a200000000000000000000000000000000000000000000000000000000000600191840191820152845161285f816004840160208901612aa3565b84519101906126c5816004840160208801612aa3565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c0000008152600082516128ad81601d850160208701612aa3565b91909101601d0192915050565b7f6d00000000000000000000000000000000000000000000000000000000000000815260008251612775816001850160208701612aa3565b6020815260008251806020840152612911816040850160208701612aa3565b601f01601f19169190910160400192915050565b604051601f8201601f1916810167ffffffffffffffff8111828210171561294e5761294e612b95565b604052919050565b6000821982111561296957612969612b53565b500190565b60008261297d5761297d612b69565b500490565b600181815b808511156129bd5781600019048211156129a3576129a3612b53565b808516156129b057918102915b93841c9390800290612987565b509250929050565b600061273683836000826129db575060016115ee565b816129e8575060006115ee565b81600181146129fe5760028114612a0857612a24565b60019150506115ee565b60ff841115612a1957612a19612b53565b50506001821b6115ee565b5060208310610133831016604e8410600b8410161715612a47575081810a6115ee565b612a518383612982565b8060001904821115612a6557612a65612b53565b029392505050565b6000816000190483118215151615612a8757612a87612b53565b500290565b600082821015612a9e57612a9e612b53565b500390565b60005b83811015612abe578181015183820152602001612aa6565b83811115612acd576000848401525b50505050565b600181811c90821680612ae757607f821691505b60208210811415612b0857634e487b7160e01b600052602260045260246000fd5b50919050565b6000600019821415612b2257612b22612b53565b5060010190565b600082612b3857612b38612b69565b500690565b634e487b7160e01b600052600160045260246000fd5b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fdfe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa2646970667358221220c9649e6d47b84a7fd8c986e39c5b0f823a4fefdb5a87a8f52d09e3f2974e2a0e64736f6c63430008060033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.