ETH Price: $2,922.05 (+0.17%)
Gas: 43 Gwei

Contract

0x95Cdb0FBf3CcaDABBa38aCC921A9b2381329f727
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

Transaction Hash
Method
Block
From
To
Value
Set Approval For...192415592024-02-16 16:12:235 days 1 hr ago1708099943IN
0x95Cdb0...1329f727
0 ETH0.0009762939.40802712
Set Approval For...191954612024-02-10 4:54:1111 days 12 hrs ago1707540851IN
0x95Cdb0...1329f727
0 ETH0.0008212433.14943135
Set Approval For...191954602024-02-10 4:53:5911 days 12 hrs ago1707540839IN
0x95Cdb0...1329f727
0 ETH0.0008475234.17715641
Set Approval For...189073652023-12-31 18:52:3551 days 22 hrs ago1704048755IN
0x95Cdb0...1329f727
0 ETH0.0006210413.28207694
Set Approval For...188988172023-12-30 14:06:4753 days 3 hrs ago1703945207IN
0x95Cdb0...1329f727
0 ETH0.0003874415.63929739
Set Approval For...188730542023-12-26 23:10:5956 days 18 hrs ago1703632259IN
0x95Cdb0...1329f727
0 ETH0.0004015116.19127886
Set Approval For...188554492023-12-24 11:53:1159 days 5 hrs ago1703418791IN
0x95Cdb0...1329f727
0 ETH0.000987521.11949653
Set Approval For...188244662023-12-20 3:32:3563 days 13 hrs ago1703043155IN
0x95Cdb0...1329f727
0 ETH0.0015655833.534342
Set Approval For...185271732023-11-08 12:15:23105 days 5 hrs ago1699445723IN
0x95Cdb0...1329f727
0 ETH0.0012623626.99100399
Safe Transfer Fr...184589582023-10-29 22:57:47114 days 18 hrs ago1698620267IN
0x95Cdb0...1329f727
0 ETH0.0007632611.53777386
Set Approval For...184010992023-10-21 20:34:35122 days 20 hrs ago1697920475IN
0x95Cdb0...1329f727
0 ETH0.000379738.13381629
Set Approval For...183560102023-10-15 13:15:35129 days 4 hrs ago1697375735IN
0x95Cdb0...1329f727
0 ETH0.00016316.58369576
Set Approval For...182745802023-10-04 3:50:11140 days 13 hrs ago1696391411IN
0x95Cdb0...1329f727
0 ETH0.000293946.29623212
Safe Transfer Fr...182745692023-10-04 3:47:59140 days 13 hrs ago1696391279IN
0x95Cdb0...1329f727
0 ETH0.000422116.29404473
Set Approval For...181021462023-09-09 23:36:11164 days 17 hrs ago1694302571IN
0x95Cdb0...1329f727
0 ETH0.0006499113.89588784
Set Approval For...180190552023-08-29 8:21:23176 days 8 hrs ago1693297283IN
0x95Cdb0...1329f727
0 ETH0.0007834216.75052041
Transfer From178119982023-07-31 9:08:59205 days 8 hrs ago1690794539IN
0x95Cdb0...1329f727
0 ETH0.0009377114.79862562
Set Approval For...177050232023-07-16 9:40:59220 days 7 hrs ago1689500459IN
0x95Cdb0...1329f727
0 ETH0.0003975715.99371243
Set Approval For...174903572023-06-16 5:35:59250 days 11 hrs ago1686893759IN
0x95Cdb0...1329f727
0 ETH0.0006854414.67446809
Set Approval For...174782792023-06-14 12:48:35252 days 4 hrs ago1686746915IN
0x95Cdb0...1329f727
0 ETH0.0007481516.0169804
Set Approval For...174522222023-06-10 20:45:47255 days 20 hrs ago1686429947IN
0x95Cdb0...1329f727
0 ETH0.0007437315.92247654
Transfer From174322202023-06-08 1:04:11258 days 16 hrs ago1686186251IN
0x95Cdb0...1329f727
0 ETH0.0011660318.39832955
Set Approval For...171586392023-04-30 11:43:35297 days 5 hrs ago1682855015IN
0x95Cdb0...1329f727
0 ETH0.0015780933.75032971
Set Approval For...171430172023-04-28 7:02:47299 days 10 hrs ago1682665367IN
0x95Cdb0...1329f727
0 ETH0.0009306137.52782943
Set Approval For...170693292023-04-17 22:04:11309 days 19 hrs ago1681769051IN
0x95Cdb0...1329f727
0 ETH0.0009986940.31240062
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Txn Hash Block From To Value
154334352022-08-29 9:54:39541 days 7 hrs ago1661766879
0x95Cdb0...1329f727
0.00000999 ETH
154334352022-08-29 9:54:39541 days 7 hrs ago1661766879
0x95Cdb0...1329f727
0 ETH
154334352022-08-29 9:54:39541 days 7 hrs ago1661766879
0x95Cdb0...1329f727
0.00000099 ETH
154334352022-08-29 9:54:39541 days 7 hrs ago1661766879
0x95Cdb0...1329f727
0 ETH
154334352022-08-29 9:54:39541 days 7 hrs ago1661766879
0x95Cdb0...1329f727
0.00693169 ETH
154334352022-08-29 9:54:39541 days 7 hrs ago1661766879
0x95Cdb0...1329f727
0 ETH
154334352022-08-29 9:54:39541 days 7 hrs ago1661766879
0x95Cdb0...1329f727
0 ETH
154334352022-08-29 9:54:39541 days 7 hrs ago1661766879
0x95Cdb0...1329f727
0 ETH
154334352022-08-29 9:54:39541 days 7 hrs ago1661766879
0x95Cdb0...1329f727
0.00000999 ETH
154334352022-08-29 9:54:39541 days 7 hrs ago1661766879
0x95Cdb0...1329f727
0 ETH
154334352022-08-29 9:54:39541 days 7 hrs ago1661766879
0x95Cdb0...1329f727
0.00000999 ETH
154334352022-08-29 9:54:39541 days 7 hrs ago1661766879
0x95Cdb0...1329f727
0 ETH
154334352022-08-29 9:54:39541 days 7 hrs ago1661766879
0x95Cdb0...1329f727
0.00000999 ETH
154334352022-08-29 9:54:39541 days 7 hrs ago1661766879
0x95Cdb0...1329f727
0 ETH
154334352022-08-29 9:54:39541 days 7 hrs ago1661766879
0x95Cdb0...1329f727
0.00000999 ETH
154334352022-08-29 9:54:39541 days 7 hrs ago1661766879
0x95Cdb0...1329f727
0 ETH
154334342022-08-29 9:54:25541 days 7 hrs ago1661766865
0x95Cdb0...1329f727
0.00000999 ETH
154334342022-08-29 9:54:25541 days 7 hrs ago1661766865
0x95Cdb0...1329f727
0 ETH
154334342022-08-29 9:54:25541 days 7 hrs ago1661766865
0x95Cdb0...1329f727
0.00000099 ETH
154334342022-08-29 9:54:25541 days 7 hrs ago1661766865
0x95Cdb0...1329f727
0 ETH
154334342022-08-29 9:54:25541 days 7 hrs ago1661766865
0x95Cdb0...1329f727
0.00000009 ETH
154334342022-08-29 9:54:25541 days 7 hrs ago1661766865
0x95Cdb0...1329f727
0 ETH
154334342022-08-29 9:54:25541 days 7 hrs ago1661766865
0x95Cdb0...1329f727
0.00000999 ETH
154334342022-08-29 9:54:25541 days 7 hrs ago1661766865
0x95Cdb0...1329f727
0 ETH
154334342022-08-29 9:54:25541 days 7 hrs ago1661766865
0x95Cdb0...1329f727
0.00000099 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Merge

Compiler Version
v0.8.9+commit.e5eed63a

Optimization Enabled:
No with 200 runs

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

pragma solidity ^0.8.4;

import './tokens/ERC721A.sol';
import './libraries/SSTORE2Map.sol';

import './SigmoidThreshold.sol';
import './RarityCompositingEngine.sol';

import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/token/common/ERC2981.sol';
import '@openzeppelin/contracts/token/ERC721/IERC721.sol';
import '@openzeppelin/contracts/utils/Strings.sol';

contract Merge is ERC721A, ERC2981, Ownable {
  using Strings for uint256;
  uint256 public MAX_MINTING_PER_BLOCK = 3;

  uint256 public deployDate;
  bool public isActive;
  SigmoidThreshold public curve;

  // Price Vars
  uint256 public a0;
  uint256 public b0;
  uint256 public c0;
  uint256 public d0;

  // Rarity Vars
  uint256 public a1;
  uint256 public b1;
  uint256 public c1;
  uint256 public d1;

  address public treasury;
  address public boostToken;

  // RCE
  RarityCompositingEngine public rce;

  uint256 public boostTokenBaseAmount = 1000;
  mapping(uint256 => uint256) public rarityTokenMap; // tokenID => rarityScore

  bool public emergencyShutdown = false;
  mapping(bytes32 => uint256) public blockMintingGuardMap; // hash(address + block number) => numMinted
  mapping(address => bool) public blacklistMap; // hash(address) => boolean

  event ChangedIsActive(bool isActive);
  event ChangedEmergencyShutdown(bool shutdown);

  struct DeployMergeNFTConfig {
    string name;
    string symbol;
    address treasury;
    address boostToken;
    address rce;
    address curve;
    uint256 a0;
    uint256 b0;
    uint256 c0;
    uint256 d0;
    uint256 a1;
    uint256 b1;
    uint256 c1;
    uint256 d1;
  }

  struct SetCurveParams {
    uint256 a0;
    uint256 b0;
    uint256 c0;
    uint256 d0;
    uint256 a1;
    uint256 b1;
    uint256 c1;
    uint256 d1;
  }

  constructor(DeployMergeNFTConfig memory config) ERC721A() {
    _name = config.name;
    _symbol = config.symbol;
    a0 = config.a0;
    b0 = config.b0;
    c0 = config.c0;
    d0 = config.d0;
    a1 = config.a1;
    b1 = config.b1;
    c1 = config.c1;
    d1 = config.d1;
    boostToken = config.boostToken;
    curve = SigmoidThreshold(config.curve);
    deployDate = block.timestamp;
    treasury = config.treasury;
    rce = RarityCompositingEngine(config.rce);
    //_transferOwnership(config.treasury);
  }

  function supportsInterface(bytes4 interfaceId)
    public
    view
    virtual
    override(ERC721A, ERC2981)
    returns (bool)
  {
    return super.supportsInterface(interfaceId);
  }

  function getTokenBalance(address token, address userAddress)
    public
    view
    returns (uint256)
  {
    return IERC721(token).balanceOf(userAddress);
  }

  function currentIndex() public view returns (uint256) {
    return _currentIndex;
  }

  function setIsActive(bool _isActive) public onlyOwner {
    isActive = _isActive;
    emit ChangedIsActive(isActive);
  }

  function setEmergencyShutdown(bool shutdown) public onlyOwner {
    emergencyShutdown = shutdown;
    emit ChangedEmergencyShutdown(shutdown);
  }

  function setBlacklist(address[] memory _list) public onlyOwner {
    for (uint256 i = 0; i < _list.length; ++i) {
      blacklistMap[_list[i]] = true;
    }
  }

  function setRoyalty(uint96 newRoyaltyFraction) public onlyOwner {
    _setDefaultRoyalty(treasury, newRoyaltyFraction);
  }

  function setMaxMinting(uint256 _max) public onlyOwner {
    MAX_MINTING_PER_BLOCK = _max;
  }

  function setDeployDate(uint256 _date) public onlyOwner {
    deployDate = _date;
  }

  function setBoostToken(address _boostToken) public onlyOwner {
    boostToken = _boostToken;
  }

  function setBoostTokenBaseAmount(uint256 _amount) public onlyOwner {
    boostTokenBaseAmount = _amount;
  }

  function setTreasury(address _treasury) public onlyOwner {
    treasury = _treasury;
  }

  function setRCE(address _rce) public onlyOwner {
    rce = RarityCompositingEngine(_rce);
  }

  function setCurve(address _curve) public onlyOwner {
    curve = SigmoidThreshold(_curve);
  }

  function setCurveParams(SetCurveParams memory config) public onlyOwner {
    a0 = config.a0;
    b0 = config.b0;
    c0 = config.c0;
    a1 = config.a1;
    b1 = config.b1;
    c1 = config.c1;
  }

  // X variable in graph. Curve is tuned to
  function numSecondsSinceDeploy() public view returns (uint256) {
    return (block.timestamp - deployDate);
  }

  function isMergeByDifficulty() public view virtual returns (bool) {
    return (block.difficulty > (2**64)) || (block.difficulty == 0);
  }

  modifier onlyIsActive() {
    require(isActive, 'minting needs to be active to mint');
    _;
  }

  modifier onlyIsNotShutdown() {
    require(!emergencyShutdown, 'emergency shutdown is in place');
    _;
  }

  modifier onlyIsNotMerge() {
    require(
      !isMergeByDifficulty(),
      'minting needs to be done before Proof of Stake'
    );
    _;
  }

  function getBoostScore(address userAddress) external view returns (uint256) {
    uint256 balance = getTokenBalance(boostToken, userAddress);
    uint256 maxBalance = balance >= 16 ? 16 : balance;
    return maxBalance * boostTokenBaseAmount;
  }

  function getRarityScoreForToken(uint256 tokenId)
    public
    view
    returns (uint256)
  {
    uint256 curr = tokenId;
    if (_startTokenId() <= curr && curr < _currentIndex) {
      while (true) {
        if (rarityTokenMap[curr] != 0) {
          return rarityTokenMap[curr];
        }
        curr--;
      }
    }
    revert OwnerQueryForNonexistentToken();
  }

  function getCurrentRarityScore(address userAddress)
    public
    view
    returns (uint256)
  {
    SigmoidThreshold.CurveParams memory config;
    config._x = numSecondsSinceDeploy();
    config.minX = a1;
    config.maxX = b1;
    config.minY = c1;
    config.maxY = d1;
    uint256 rarity = curve.getY(config);
    try this.getBoostScore(userAddress) returns (uint256 boost) {
      return rarity + boost;
    } catch {
      return rarity;
    }
  }

  function getCurrentPrice() public view returns (uint256) {
    SigmoidThreshold.CurveParams memory config;
    config._x = numSecondsSinceDeploy();
    config.minX = a0;
    config.maxX = b0;
    config.minY = c0;
    config.maxY = d0;
    uint256 price = curve.getY(config);
    return price; // in GWEI
  }

  function contractURI() public view returns (string memory) {
    return
      string(
        abi.encodePacked(
          'data:application/json;base64,',
          Base64.encode(
            abi.encodePacked(
              '{"name":"',
              _name,
              '", "description": "A Proof of Beauty project. Fully on-chain generative statues to remember the MERGE.',
              '", "external_link": "https://merge.pob.studio/',
              '", "image": "https://merge.pob.studio/assets/logo.png" }'
            )
          )
        )
      );
  }

  function tokenURI(uint256 tokenId)
    public
    view
    virtual
    override
    returns (string memory)
  {
    require(_exists(tokenId), 'URI query for nonexistent token');
    uint256 rarityScore = getRarityScoreForToken(tokenId);
    bytes memory seed = abi.encodePacked(rarityScore, tokenId);
    (, uint16[] memory attributeIndexes) = rce.getRarity(rarityScore, seed);
    string memory image = rce.getRender(attributeIndexes);

    return
      string(
        abi.encodePacked(
          'data:application/json;base64,',
          Base64.encode(
            abi.encodePacked(
              '{"name": "Statue #',
              tokenId.toString(),
              '", "description": "',
              'A Proof of Beauty project. Fully on-chain generative statues to remember the MERGE.',
              '", "image": "',
              image,
              '", "aspect_ratio": "1',
              '", "attributes": ',
              rce.getAttributesJSON(attributeIndexes),
              '}'
            )
          )
        )
      );
  }

  function mint(address to, uint256 numMints)
    public
    payable
    onlyIsActive
    onlyIsNotMerge
    onlyIsNotShutdown
  {
    bytes32 blockNumHash = keccak256(abi.encode(block.number, msg.sender));
    require(
      blockMintingGuardMap[blockNumHash] + numMints <= MAX_MINTING_PER_BLOCK,
      'exceeded max number of mints'
    );
    require(!blacklistMap[msg.sender], 'caller is blacklisted');
    uint256 totalPrice = getCurrentPrice() * numMints;
    require(totalPrice <= msg.value, 'insufficient funds to pay for mint');
    uint256 currentRarityScore = getCurrentRarityScore(msg.sender);
    rarityTokenMap[_currentIndex] = currentRarityScore;
    blockMintingGuardMap[blockNumHash] =
      blockMintingGuardMap[blockNumHash] +
      numMints;
    _mint(to, numMints, '', false);
    treasury.call{value: totalPrice}('');
    payable(msg.sender).transfer(msg.value - totalPrice);
  }
}

File 2 of 27 : ERC721A.sol
// SPDX-License-Identifier: MIT
// Creator: Chiru Labs

pragma solidity ^0.8.4;

import '@openzeppelin/contracts/token/ERC721/IERC721.sol';
import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol';
import '@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol';
import '@openzeppelin/contracts/utils/Address.sol';
import '@openzeppelin/contracts/utils/Context.sol';
import '@openzeppelin/contracts/utils/Strings.sol';
import '@openzeppelin/contracts/utils/introspection/ERC165.sol';

error ApprovalCallerNotOwnerNorApproved();
error ApprovalQueryForNonexistentToken();
error ApproveToCaller();
error ApprovalToCurrentOwner();
error BalanceQueryForZeroAddress();
error MintToZeroAddress();
error MintZeroQuantity();
error OwnerQueryForNonexistentToken();
error TransferCallerNotOwnerNorApproved();
error TransferFromIncorrectOwner();
error TransferToNonERC721ReceiverImplementer();
error TransferToZeroAddress();
error URIQueryForNonexistentToken();

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension. Built to optimize for lower gas during batch mints.
 *
 * Assumes serials are sequentially minted starting at _startTokenId() (defaults to 0, e.g. 0, 1, 2, 3..).
 *
 * Assumes that an owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
 *
 * Assumes that the maximum token id cannot exceed 2**256 - 1 (max value of uint256).
 */
contract ERC721A is Context, ERC165, IERC721, IERC721Metadata {
  using Address for address;
  using Strings for uint256;

  // Compiler will pack this into a single 256bit word.
  struct TokenOwnership {
    // The address of the owner.
    address addr;
    // Keeps track of the start time of ownership with minimal overhead for tokenomics.
    uint64 startTimestamp;
    // Whether the token has been burned.
    bool burned;
  }

  // Compiler will pack this into a single 256bit word.
  struct AddressData {
    // Realistically, 2**64-1 is more than enough.
    uint64 balance;
    // Keeps track of mint count with minimal overhead for tokenomics.
    uint64 numberMinted;
    // Keeps track of burn count with minimal overhead for tokenomics.
    uint64 numberBurned;
    // For miscellaneous variable(s) pertaining to the address
    // (e.g. number of whitelist mint slots used).
    // If there are multiple variables, please pack them into a uint64.
    uint64 aux;
  }

  // The tokenId of the next token to be minted.
  uint256 internal _currentIndex;

  // The number of tokens burned.
  uint256 internal _burnCounter;

  // Token name
  string internal _name;

  // Token symbol
  string internal _symbol;

  // Mapping from token ID to ownership details
  // An empty struct value does not necessarily mean the token is unowned. See _ownershipOf implementation for details.
  mapping(uint256 => TokenOwnership) internal _ownerships;

  // Mapping owner address to address data
  mapping(address => AddressData) private _addressData;

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

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

  constructor() {
    _currentIndex = _startTokenId();
  }

  /**
   * To change the starting tokenId, please override this function.
   */
  function _startTokenId() internal view virtual returns (uint256) {
    return 0;
  }

  /**
   * @dev Burned tokens are calculated here, use _totalMinted() if you want to count just minted tokens.
   */
  function totalSupply() public view returns (uint256) {
    // Counter underflow is impossible as _burnCounter cannot be incremented
    // more than _currentIndex - _startTokenId() times
    unchecked {
      return _currentIndex - _burnCounter - _startTokenId();
    }
  }

  /**
   * Returns the total amount of tokens minted in the contract.
   */
  function _totalMinted() internal view returns (uint256) {
    // Counter underflow is impossible as _currentIndex does not decrement,
    // and it is initialized to _startTokenId()
    unchecked {
      return _currentIndex - _startTokenId();
    }
  }

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

  /**
   * @dev See {IERC721-balanceOf}.
   */
  function balanceOf(address owner) public view override returns (uint256) {
    if (owner == address(0)) revert BalanceQueryForZeroAddress();
    return uint256(_addressData[owner].balance);
  }

  /**
   * Returns the number of tokens minted by `owner`.
   */
  function _numberMinted(address owner) internal view returns (uint256) {
    return uint256(_addressData[owner].numberMinted);
  }

  /**
   * Returns the number of tokens burned by or on behalf of `owner`.
   */
  function _numberBurned(address owner) internal view returns (uint256) {
    return uint256(_addressData[owner].numberBurned);
  }

  /**
   * Returns the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
   */
  function _getAux(address owner) internal view returns (uint64) {
    return _addressData[owner].aux;
  }

  /**
   * Sets the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
   * If there are multiple variables, please pack them into a uint64.
   */
  function _setAux(address owner, uint64 aux) internal {
    _addressData[owner].aux = aux;
  }

  /**
   * Gas spent here starts off proportional to the maximum mint batch size.
   * It gradually moves to O(1) as tokens get transferred around in the collection over time.
   */
  function _ownershipOf(uint256 tokenId)
    internal
    view
    returns (TokenOwnership memory)
  {
    uint256 curr = tokenId;

    unchecked {
      if (_startTokenId() <= curr && curr < _currentIndex) {
        TokenOwnership memory ownership = _ownerships[curr];
        if (!ownership.burned) {
          if (ownership.addr != address(0)) {
            return ownership;
          }
          // Invariant:
          // There will always be an ownership that has an address and is not burned
          // before an ownership that does not have an address and is not burned.
          // Hence, curr will not underflow.
          while (true) {
            curr--;
            ownership = _ownerships[curr];
            if (ownership.addr != address(0)) {
              return ownership;
            }
          }
        }
      }
    }
    revert OwnerQueryForNonexistentToken();
  }

  /**
   * @dev See {IERC721-ownerOf}.
   */
  function ownerOf(uint256 tokenId) public view override returns (address) {
    return _ownershipOf(tokenId).addr;
  }

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

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

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

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

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

  /**
   * @dev See {IERC721-approve}.
   */
  function approve(address to, uint256 tokenId) public override {
    address owner = ERC721A.ownerOf(tokenId);
    if (to == owner) revert ApprovalToCurrentOwner();

    if (_msgSender() != owner && !isApprovedForAll(owner, _msgSender())) {
      revert ApprovalCallerNotOwnerNorApproved();
    }

    _approve(to, tokenId, owner);
  }

  /**
   * @dev See {IERC721-getApproved}.
   */
  function getApproved(uint256 tokenId) public view override returns (address) {
    if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();

    return _tokenApprovals[tokenId];
  }

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

    _operatorApprovals[_msgSender()][operator] = approved;
    emit ApprovalForAll(_msgSender(), operator, approved);
  }

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

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

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

  /**
   * @dev See {IERC721-safeTransferFrom}.
   */
  function safeTransferFrom(
    address from,
    address to,
    uint256 tokenId,
    bytes memory _data
  ) public virtual override {
    _transfer(from, to, tokenId);
    if (
      to.isContract() &&
      !_checkContractOnERC721Received(from, to, tokenId, _data)
    ) {
      revert TransferToNonERC721ReceiverImplementer();
    }
  }

  /**
   * @dev Returns whether `tokenId` exists.
   *
   * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
   *
   * Tokens start existing when they are minted (`_mint`),
   */
  function _exists(uint256 tokenId) internal view returns (bool) {
    return
      _startTokenId() <= tokenId &&
      tokenId < _currentIndex &&
      !_ownerships[tokenId].burned;
  }

  function _safeMint(address to, uint256 quantity) internal {
    _safeMint(to, quantity, '');
  }

  /**
   * @dev Safely mints `quantity` tokens and transfers them to `to`.
   *
   * Requirements:
   *
   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
   * - `quantity` must be greater than 0.
   *
   * Emits a {Transfer} event.
   */
  function _safeMint(
    address to,
    uint256 quantity,
    bytes memory _data
  ) internal {
    _mint(to, quantity, _data, true);
  }

  /**
   * @dev Mints `quantity` tokens and transfers them to `to`.
   *
   * Requirements:
   *
   * - `to` cannot be the zero address.
   * - `quantity` must be greater than 0.
   *
   * Emits a {Transfer} event.
   */
  function _mint(
    address to,
    uint256 quantity,
    bytes memory _data,
    bool safe
  ) internal {
    uint256 startTokenId = _currentIndex;
    if (to == address(0)) revert MintToZeroAddress();
    if (quantity == 0) revert MintZeroQuantity();

    _beforeTokenTransfers(address(0), to, startTokenId, quantity);

    // Overflows are incredibly unrealistic.
    // balance or numberMinted overflow if current value of either + quantity > 1.8e19 (2**64) - 1
    // updatedIndex overflows if _currentIndex + quantity > 1.2e77 (2**256) - 1
    unchecked {
      _addressData[to].balance += uint64(quantity);
      _addressData[to].numberMinted += uint64(quantity);

      _ownerships[startTokenId].addr = to;
      _ownerships[startTokenId].startTimestamp = uint64(block.timestamp);

      uint256 updatedIndex = startTokenId;
      uint256 end = updatedIndex + quantity;

      if (safe && to.isContract()) {
        do {
          emit Transfer(address(0), to, updatedIndex);
          if (
            !_checkContractOnERC721Received(
              address(0),
              to,
              updatedIndex++,
              _data
            )
          ) {
            revert TransferToNonERC721ReceiverImplementer();
          }
        } while (updatedIndex != end);
        // Reentrancy protection
        if (_currentIndex != startTokenId) revert();
      } else {
        do {
          emit Transfer(address(0), to, updatedIndex++);
        } while (updatedIndex != end);
      }
      _currentIndex = updatedIndex;
    }
    _afterTokenTransfers(address(0), to, startTokenId, quantity);
  }

  /**
   * @dev Transfers `tokenId` from `from` to `to`.
   *
   * Requirements:
   *
   * - `to` cannot be the zero address.
   * - `tokenId` token must be owned by `from`.
   *
   * Emits a {Transfer} event.
   */
  function _transfer(
    address from,
    address to,
    uint256 tokenId
  ) private {
    TokenOwnership memory prevOwnership = _ownershipOf(tokenId);

    if (prevOwnership.addr != from) revert TransferFromIncorrectOwner();

    bool isApprovedOrOwner = (_msgSender() == from ||
      isApprovedForAll(from, _msgSender()) ||
      getApproved(tokenId) == _msgSender());

    if (!isApprovedOrOwner) revert TransferCallerNotOwnerNorApproved();
    if (to == address(0)) revert TransferToZeroAddress();

    _beforeTokenTransfers(from, to, tokenId, 1);

    // Clear approvals from the previous owner
    _approve(address(0), tokenId, from);

    // Underflow of the sender's balance is impossible because we check for
    // ownership above and the recipient's balance can't realistically overflow.
    // Counter overflow is incredibly unrealistic as tokenId would have to be 2**256.
    unchecked {
      _addressData[from].balance -= 1;
      _addressData[to].balance += 1;

      TokenOwnership storage currSlot = _ownerships[tokenId];
      currSlot.addr = to;
      currSlot.startTimestamp = uint64(block.timestamp);

      // If the ownership slot of tokenId+1 is not explicitly set, that means the transfer initiator owns it.
      // Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.
      uint256 nextTokenId = tokenId + 1;
      TokenOwnership storage nextSlot = _ownerships[nextTokenId];
      if (nextSlot.addr == address(0)) {
        // This will suffice for checking _exists(nextTokenId),
        // as a burned slot cannot contain the zero address.
        if (nextTokenId != _currentIndex) {
          nextSlot.addr = from;
          nextSlot.startTimestamp = prevOwnership.startTimestamp;
        }
      }
    }

    emit Transfer(from, to, tokenId);
    _afterTokenTransfers(from, to, tokenId, 1);
  }

  /**
   * @dev This is equivalent to _burn(tokenId, false)
   */
  function _burn(uint256 tokenId) internal virtual {
    _burn(tokenId, false);
  }

  /**
   * @dev Destroys `tokenId`.
   * The approval is cleared when the token is burned.
   *
   * Requirements:
   *
   * - `tokenId` must exist.
   *
   * Emits a {Transfer} event.
   */
  function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
    TokenOwnership memory prevOwnership = _ownershipOf(tokenId);

    address from = prevOwnership.addr;

    if (approvalCheck) {
      bool isApprovedOrOwner = (_msgSender() == from ||
        isApprovedForAll(from, _msgSender()) ||
        getApproved(tokenId) == _msgSender());

      if (!isApprovedOrOwner) revert TransferCallerNotOwnerNorApproved();
    }

    _beforeTokenTransfers(from, address(0), tokenId, 1);

    // Clear approvals from the previous owner
    _approve(address(0), tokenId, from);

    // Underflow of the sender's balance is impossible because we check for
    // ownership above and the recipient's balance can't realistically overflow.
    // Counter overflow is incredibly unrealistic as tokenId would have to be 2**256.
    unchecked {
      AddressData storage addressData = _addressData[from];
      addressData.balance -= 1;
      addressData.numberBurned += 1;

      // Keep track of who burned the token, and the timestamp of burning.
      TokenOwnership storage currSlot = _ownerships[tokenId];
      currSlot.addr = from;
      currSlot.startTimestamp = uint64(block.timestamp);
      currSlot.burned = true;

      // If the ownership slot of tokenId+1 is not explicitly set, that means the burn initiator owns it.
      // Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.
      uint256 nextTokenId = tokenId + 1;
      TokenOwnership storage nextSlot = _ownerships[nextTokenId];
      if (nextSlot.addr == address(0)) {
        // This will suffice for checking _exists(nextTokenId),
        // as a burned slot cannot contain the zero address.
        if (nextTokenId != _currentIndex) {
          nextSlot.addr = from;
          nextSlot.startTimestamp = prevOwnership.startTimestamp;
        }
      }
    }

    emit Transfer(from, address(0), tokenId);
    _afterTokenTransfers(from, address(0), tokenId, 1);

    // Overflow not possible, as _burnCounter cannot be exceed _currentIndex times.
    unchecked {
      _burnCounter++;
    }
  }

  /**
   * @dev Approve `to` to operate on `tokenId`
   *
   * Emits a {Approval} event.
   */
  function _approve(
    address to,
    uint256 tokenId,
    address owner
  ) private {
    _tokenApprovals[tokenId] = to;
    emit Approval(owner, to, tokenId);
  }

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

  /**
   * @dev Hook that is called before a set of serially-ordered token ids are about to be transferred. This includes minting.
   * And also called before burning one token.
   *
   * startTokenId - the first token id to be transferred
   * quantity - the amount to be transferred
   *
   * Calling conditions:
   *
   * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
   * transferred to `to`.
   * - When `from` is zero, `tokenId` will be minted for `to`.
   * - When `to` is zero, `tokenId` will be burned by `from`.
   * - `from` and `to` are never both zero.
   */
  function _beforeTokenTransfers(
    address from,
    address to,
    uint256 startTokenId,
    uint256 quantity
  ) internal virtual {}

  /**
   * @dev Hook that is called after a set of serially-ordered token ids have been transferred. This includes
   * minting.
   * And also called after one token has been burned.
   *
   * startTokenId - the first token id to be transferred
   * quantity - the amount to be transferred
   *
   * Calling conditions:
   *
   * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
   * transferred to `to`.
   * - When `from` is zero, `tokenId` has been minted for `to`.
   * - When `to` is zero, `tokenId` has been burned by `from`.
   * - `from` and `to` are never both zero.
   */
  function _afterTokenTransfers(
    address from,
    address to,
    uint256 startTokenId,
    uint256 quantity
  ) internal virtual {}
}

File 3 of 27 : SSTORE2Map.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import './Create3.sol';

import './Bytecode.sol';

/**
  @title A write-once key-value storage for storing chunks of data with a lower write & read cost.
  @author Agustin Aguilar <[email protected]>
  Readme: https://github.com/0xsequence/sstore2#readme
*/
library SSTORE2Map {
  error WriteError();

  //                                         keccak256(bytes('@0xSequence.SSTORE2Map.slot'))
  bytes32 private constant SLOT_KEY_PREFIX =
    0xd351a9253491dfef66f53115e9e3afda3b5fdef08a1de6937da91188ec553be5;

  function internalKey(bytes32 _key) internal pure returns (bytes32) {
    // Mutate the key so it doesn't collide
    // if the contract is also using CREATE3 for other things
    return keccak256(abi.encode(SLOT_KEY_PREFIX, _key));
  }

  /**
    @notice Stores `_data` and returns `pointer` as key for later retrieval
    @dev The pointer is a contract address with `_data` as code
    @param _data To be written
    @param _key unique string key for accessing the written data (can only be used once)
    @return pointer Pointer to the written `_data`
  */
  function write(string memory _key, bytes memory _data)
    internal
    returns (address pointer)
  {
    return write(keccak256(bytes(_key)), _data);
  }

  /**
    @notice Stores `_data` and returns `pointer` as key for later retrieval
    @dev The pointer is a contract address with `_data` as code
    @param _data to be written
    @param _key unique bytes32 key for accessing the written data (can only be used once)
    @return pointer Pointer to the written `_data`
  */
  function write(bytes32 _key, bytes memory _data)
    internal
    returns (address pointer)
  {
    // Append 00 to _data so contract can't be called
    // Build init code
    bytes memory code = Bytecode.creationCodeFor(
      abi.encodePacked(hex'00', _data)
    );

    // Deploy contract using create3
    pointer = Create3.create3(internalKey(_key), code);
  }

  /**
    @notice Reads the contents for a given `_key`, it maps to a contract code as data, skips the first byte
    @dev The function is intended for reading pointers first written by `write`
    @param _key string key that constains the data
    @return data read from contract associated with `_key`
  */
  function read(string memory _key) internal view returns (bytes memory) {
    return read(keccak256(bytes(_key)));
  }

  /**
    @notice Reads the contents for a given `_key`, it maps to a contract code as data, skips the first byte
    @dev The function is intended for reading pointers first written by `write`
    @param _key string key that constains the data
    @param _start number of bytes to skip
    @return data read from contract associated with `_key`
  */
  function read(string memory _key, uint256 _start)
    internal
    view
    returns (bytes memory)
  {
    return read(keccak256(bytes(_key)), _start);
  }

  /**
    @notice Reads the contents for a given `_key`, it maps to a contract code as data, skips the first byte
    @dev The function is intended for reading pointers first written by `write`
    @param _key string key that constains the data
    @param _start number of bytes to skip
    @param _end index before which to end extraction
    @return data read from contract associated with `_key`
  */
  function read(
    string memory _key,
    uint256 _start,
    uint256 _end
  ) internal view returns (bytes memory) {
    return read(keccak256(bytes(_key)), _start, _end);
  }

  /**
    @notice Reads the contents for a given `_key`, it maps to a contract code as data, skips the first byte
    @dev The function is intended for reading pointers first written by `write`
    @param _key bytes32 key that constains the data
    @return data read from contract associated with `_key`
  */
  function read(bytes32 _key) internal view returns (bytes memory) {
    return
      Bytecode.codeAt(
        Create3.addressOf(internalKey(_key)),
        1,
        type(uint256).max
      );
  }

  /**
    @notice Reads the contents for a given `_key`, it maps to a contract code as data, skips the first byte
    @dev The function is intended for reading pointers first written by `write`
    @param _key bytes32 key that constains the data
    @param _start number of bytes to skip
    @return data read from contract associated with `_key`
  */
  function read(bytes32 _key, uint256 _start)
    internal
    view
    returns (bytes memory)
  {
    return
      Bytecode.codeAt(
        Create3.addressOf(internalKey(_key)),
        _start + 1,
        type(uint256).max
      );
  }

  /**
    @notice Reads the contents for a given `_key`, it maps to a contract code as data, skips the first byte
    @dev The function is intended for reading pointers first written by `write`
    @param _key bytes32 key that constains the data
    @param _start number of bytes to skip
    @param _end index before which to end extraction
    @return data read from contract associated with `_key`
  */
  function read(
    bytes32 _key,
    uint256 _start,
    uint256 _end
  ) internal view returns (bytes memory) {
    return
      Bytecode.codeAt(
        Create3.addressOf(internalKey(_key)),
        _start + 1,
        _end + 1
      );
  }
}

File 4 of 27 : SigmoidThreshold.sol
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.4;

import '@openzeppelin/contracts/utils/math/Math.sol';
import '@openzeppelin/contracts/utils/math/SafeMath.sol';
import '@openzeppelin/contracts/access/Ownable.sol';

contract SigmoidThreshold {
  using SafeMath for uint256;
  using SafeMath for uint64;

  struct CurveParams {
    uint256 _x;
    uint256 minX;
    uint256 maxX;
    uint256 minY;
    uint256 maxY;
  }

  uint256[23] private slots;

  constructor() {
    slots[0] = 1000000000000000000;
    slots[1] = 994907149075715143;
    slots[2] = 988513057369406817;
    slots[3] = 982013790037908452;
    slots[4] = 970687769248643639;
    slots[5] = 952574126822433143;
    slots[6] = 924141819978756551;
    slots[7] = 880797077977882314;
    slots[8] = 817574476193643651;
    slots[9] = 731058578630004896;
    slots[10] = 622459331201854593;
    slots[11] = 500000000000000000;
    slots[12] = 377540668798145407;
    slots[13] = 268941421369995104;
    slots[14] = 182425523806356349;
    slots[15] = 119202922022117574;
    slots[16] = 75858180021243560;
    slots[17] = 47425873177566788;
    slots[18] = 29312230751356326;
    slots[19] = 17986209962091562;
    slots[20] = 11486942630593183;
    slots[21] = 5092850924284857;
    slots[22] = 0;
  }

  function getY(CurveParams memory config) public view returns (uint256) {
    if (config._x <= config.minX) {
      return config.minY;
    }
    if (config._x >= config.maxX) {
      return config.maxY;
    }

    uint256 slotWidth = config.maxX.sub(config.minX).div(slots.length);
    uint256 xa = config._x.sub(config.minX).div(slotWidth);
    uint256 xb = Math.min(xa.add(1), slots.length.sub(1));

    uint256 slope = slots[xa].sub(slots[xb]).mul(1e18).div(slotWidth);
    uint256 wy = slots[xa].add(slope.mul(slotWidth.mul(xa)).div(1e18));

    uint256 percentage = 0;
    if (wy > slope.mul(config._x).div(1e18)) {
      percentage = wy.sub(slope.mul(config._x).div(1e18));
    } else {
      percentage = slope.mul(config._x).div(1e18).sub(wy);
    }

    uint256 result = config.minY.add(
      config.maxY.sub(config.minY).mul(percentage).div(1e18)
    );

    return config.maxY.sub(result); // inverse curve to be LOW => HIGH
  }
}

File 5 of 27 : RarityCompositingEngine.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;
import './RendererPropsStorage.sol';
import './ChunkedDataStorage.sol';

import '@openzeppelin/contracts/utils/Strings.sol';
import '@openzeppelin/contracts/utils/Base64.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import '@abf-monorepo/protocol/contracts/renderers/LayerCompositeRenderer.sol';
import '@abf-monorepo/protocol/contracts/libraries/BytesUtils.sol';

contract RarityCompositingEngine is Ownable {
  uint256 public constant MAX_UINT_16 = 0xFFFF;
  uint256 public constant RARITY_DATA_TUPLE_NUM_BYTES = 4;

  address public immutable COMPOSITING_RENDERER;
  address public immutable ATTRIBUTE_RENDERER;
  uint256 public immutable MAX_LAYERS;
  bytes public GLOBAL_ATTRIBUTE_PREFIX;

  // storage contracts
  RendererPropsStorage public rendererPropsStorage;
  ChunkedDataStorage public layerStorage;
  ChunkedDataStorage public attributeStorage;

  uint256 constant NUM_DECIMALS = 2; // units are in bps
  uint256 constant ONE_UNIT = 10**NUM_DECIMALS; // represents 0.01x
  uint256 constant ONE_HUNDRED_PERCENT = 100 * ONE_UNIT; // represents 1.00x

  struct LayerData {
    uint8 layerType;
    bytes rarityData;
  }

  struct AttributeData {
    bool shouldShowInAttributes;
    bool shouldShowInRendererProps;
    uint16 rendererDataIndex;
    string key;
    string value;
    bytes prefix;
  }

  constructor(
    uint256 _maxLayers,
    address _compositingRenderer,
    address _attributeRenderer,
    bytes memory _globalAttributePrefix,
    address _rendererPropsStorage,
    address _layerStorage,
    address _attributeStorage
  ) {
    MAX_LAYERS = _maxLayers;
    COMPOSITING_RENDERER = _compositingRenderer;
    ATTRIBUTE_RENDERER = _attributeRenderer;
    GLOBAL_ATTRIBUTE_PREFIX = _globalAttributePrefix;
    rendererPropsStorage = RendererPropsStorage(_rendererPropsStorage);
    layerStorage = ChunkedDataStorage(_layerStorage);
    attributeStorage = ChunkedDataStorage(_attributeStorage);
  }

  function setRendererPropsStorage(address _rendererPropsStorage)
    public
    onlyOwner
  {
    rendererPropsStorage = RendererPropsStorage(_rendererPropsStorage);
  }

  function setLayerStorage(address _layerStorage) public onlyOwner {
    layerStorage = ChunkedDataStorage(_layerStorage);
  }

  function setAttributeStorage(address _attributeStorage) public onlyOwner {
    attributeStorage = ChunkedDataStorage(_attributeStorage);
  }

  function decodeLayerData(bytes memory data)
    public
    pure
    returns (LayerData memory ld)
  {
    if (data.length != 0) {
      ld.layerType = uint8(data[0]);
      ld.rarityData = BytesUtils.slice(data, 1, data.length - 1);
    }
  }

  function decodeAttributeData(bytes memory data)
    public
    pure
    returns (AttributeData memory ad)
  {
    if (data.length != 0) {
      ad.shouldShowInAttributes = uint8(data[0]) == 1;
      ad.shouldShowInRendererProps = uint8(data[1]) == 1;
      ad.rendererDataIndex = BytesUtils.toUint16(data, 2);
      uint8 keyLength = uint8(data[4]);
      ad.key = string(BytesUtils.slice(data, 5, keyLength));
      uint8 valueLength = uint8(data[5 + keyLength]);
      ad.value = string(BytesUtils.slice(data, 6 + keyLength, valueLength));
      ad.prefix = BytesUtils.slice(
        data,
        6 + keyLength + valueLength,
        data.length - (6 + keyLength + valueLength)
      );
    }
  }

  function resolveLayerIndex(
    uint16[] memory attributeIndexes,
    uint256 layerIndex
  ) public view returns (uint256) {
    require(
      layerIndex != 0,
      'RarityCompositingEngine: layerIndex can not be zero'
    );
    LayerData memory layerData = decodeLayerData(
      layerStorage.indexToData(layerIndex)
    );
    if (layerData.layerType == 1) {
      uint16 rootAttributeIndex = attributeIndexes[
        BytesUtils.toUint16(layerData.rarityData, 0)
      ];
      require(
        rootAttributeIndex != 0,
        'RarityCompositingEngine: Root attribute has not been set yet'
      );
      uint256 dependentLayerIndex = 0;
      for (uint256 j = 2; j < layerData.rarityData.length; j += 4) {
        if (
          BytesUtils.toUint16(layerData.rarityData, j) == rootAttributeIndex
        ) {
          dependentLayerIndex = BytesUtils.toUint16(
            layerData.rarityData,
            j + 2
          );
          break;
        }
      }
      require(
        dependentLayerIndex != 0,
        'RarityCompositingEngine: No dependent layerIndex was found'
      );
      return resolveLayerIndex(attributeIndexes, dependentLayerIndex);
    } else if (layerData.layerType == 0) {
      return layerIndex;
    }
    return 0;
  }

  function applyRarityMultiplier(
    uint256 rarityMultiplier,
    uint256[] memory rarityData
  )
    public
    pure
    returns (
      uint256 appliedRaritySum,
      uint256[] memory appliedRarityData,
      uint256[] memory rarityMultipliers
    )
  {
    rarityMultipliers = new uint256[](rarityData.length);

    if (rarityData.length == 0) {
      return (0, rarityData, rarityMultipliers);
    }

    if (rarityData.length == 2) {
      return (rarityData[1], rarityData, rarityMultipliers);
    }

    // sum the current rarity values
    uint256 highestRarityWeight = rarityData[1];
    uint256 lowestRarityWeight = rarityData[rarityData.length - 1];

    if (highestRarityWeight - lowestRarityWeight == 0) {
      for (uint256 i = 1; i < rarityData.length; i += 2) {
        appliedRaritySum += rarityData[i];
      }
      return (appliedRaritySum, rarityData, rarityMultipliers);
    }

    appliedRarityData = rarityData;

    uint256 a = (rarityMultiplier * ONE_UNIT) /
      ((highestRarityWeight - lowestRarityWeight)**2);

    for (uint256 i = 1; i < rarityData.length; i += 2) {
      uint256 scaledRarityMultiplier = (a *
        (highestRarityWeight - rarityData[i])**2) / ONE_UNIT;
      uint256 appliedRarity = ((ONE_HUNDRED_PERCENT + scaledRarityMultiplier) *
        rarityData[i]) / ONE_UNIT;
      rarityMultipliers[i] = scaledRarityMultiplier;
      appliedRarityData[i] = appliedRarity;
      appliedRaritySum += appliedRarityData[i];
    }
  }

  function getRarity(uint256 rarityMultiplier, bytes memory seed)
    public
    view
    returns (uint256[] memory layerIndexes, uint16[] memory attributeIndexes)
  {
    // attributeIndexes are the keys to the actual visual output data for a specific layer index
    attributeIndexes = new uint16[](MAX_LAYERS);

    // layerIndexes are the keys to the actual layer and its corresponding rarity data
    layerIndexes = new uint256[](MAX_LAYERS);
    // set default layer indexes
    for (uint16 i = 0; i < MAX_LAYERS; ++i) {
      layerIndexes[i] = i + 1;
    }

    for (uint16 i = 0; i < MAX_LAYERS; ++i) {
      layerIndexes[i] = resolveLayerIndex(attributeIndexes, layerIndexes[i]);
      LayerData memory layerData = decodeLayerData(
        layerStorage.indexToData(layerIndexes[i])
      );
      uint256 randomSource = uint256(keccak256(abi.encodePacked(seed, i)));

      uint256[] memory rarityData = new uint256[](
        (layerData.rarityData.length) / 2
      );
      for (uint256 j = 0; j < layerData.rarityData.length; j += 2) {
        rarityData[j / 2] = BytesUtils.toUint16(layerData.rarityData, j);
      }

      (
        uint256 appliedRaritySum,
        uint256[] memory appliedRarityData,

      ) = applyRarityMultiplier(rarityMultiplier, rarityData);
      // get attribute for this layer
      uint16 attributeIndex = 0;
      uint256 rarityValue = randomSource % appliedRaritySum;
      uint256 acc = 0;
      for (uint256 j = 1; j < appliedRarityData.length; j += 2) {
        acc += appliedRarityData[j];
        if (acc >= rarityValue) {
          attributeIndex = uint16(appliedRarityData[j - 1]);
          break;
        }
      }
      require(
        attributeIndex != 0,
        'RarityCompositingEngine: No attribute was found for layer.'
      );
      attributeIndexes[i] = attributeIndex;
    }
  }

  function getRendererProps(uint16[] memory attributeIndexes)
    public
    view
    returns (address[] memory renderers, bytes[] memory rendererProps)
  {
    uint256 numNonrendereredAttributes = 0;
    for (uint256 i = 0; i < attributeIndexes.length; ++i) {
      AttributeData memory ad = decodeAttributeData(
        attributeStorage.indexToData(attributeIndexes[i])
      );
      if (!ad.shouldShowInRendererProps) {
        numNonrendereredAttributes++;
      }
    }

    renderers = new address[](
      attributeIndexes.length - numNonrendereredAttributes
    );
    rendererProps = new bytes[](
      attributeIndexes.length - numNonrendereredAttributes
    );

    uint256 numRenderersStored = 0;
    for (uint256 i = 0; i < attributeIndexes.length; ++i) {
      AttributeData memory ad = decodeAttributeData(
        attributeStorage.indexToData(attributeIndexes[i])
      );
      if (ad.shouldShowInRendererProps) {
        uint256 rendererIndex = attributeIndexes.length -
          numNonrendereredAttributes -
          1 -
          numRenderersStored;
        renderers[rendererIndex] = ATTRIBUTE_RENDERER;
        rendererProps[rendererIndex] = abi.encodePacked(
          GLOBAL_ATTRIBUTE_PREFIX,
          ad.prefix,
          rendererPropsStorage.indexToRendererProps(ad.rendererDataIndex)
        );
        numRenderersStored++;
      }
    }
  }

  function getAttributesJSON(uint16[] memory attributeIndexes)
    public
    view
    returns (string memory)
  {
    bytes memory attributes = '[';
    for (uint256 i = 0; i < attributeIndexes.length; ++i) {
      AttributeData memory ad = decodeAttributeData(
        attributeStorage.indexToData(attributeIndexes[i])
      );
      if (ad.shouldShowInAttributes) {
        attributes = abi.encodePacked(
          attributes,
          (attributes.length == 1) ? '' : ',',
          '{"value":"',
          ad.value,
          '","trait_type":"',
          ad.key,
          '"}'
        );
      }
    }
    attributes = abi.encodePacked(attributes, ']');
    return string(attributes);
  }

  function getRender(uint16[] memory attributeIndexes)
    public
    view
    returns (string memory)
  {
    LayerCompositeRenderer renderer = LayerCompositeRenderer(
      COMPOSITING_RENDERER
    );
    (
      address[] memory renderers,
      bytes[] memory rendererProps
    ) = getRendererProps(attributeIndexes);
    return renderer.render(renderer.encodeProps(renderers, rendererProps));
  }

  function getRenderRaw(uint16[] memory attributeIndexes)
    public
    view
    returns (bytes memory)
  {
    LayerCompositeRenderer renderer = LayerCompositeRenderer(
      COMPOSITING_RENDERER
    );
    (
      address[] memory renderers,
      bytes[] memory rendererProps
    ) = getRendererProps(attributeIndexes);
    return renderer.renderRaw(renderer.encodeProps(renderers, rendererProps));
  }
}

File 6 of 27 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 7 of 27 : ERC2981.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/common/ERC2981.sol)

pragma solidity ^0.8.0;

import "../../interfaces/IERC2981.sol";
import "../../utils/introspection/ERC165.sol";

/**
 * @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
 *
 * Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
 * specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
 *
 * Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
 * fee is specified in basis points by default.
 *
 * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
 * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to
 * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
 *
 * _Available since v4.5._
 */
abstract contract ERC2981 is IERC2981, ERC165 {
    struct RoyaltyInfo {
        address receiver;
        uint96 royaltyFraction;
    }

    RoyaltyInfo private _defaultRoyaltyInfo;
    mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo;

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

    /**
     * @inheritdoc IERC2981
     */
    function royaltyInfo(uint256 _tokenId, uint256 _salePrice) public view virtual override returns (address, uint256) {
        RoyaltyInfo memory royalty = _tokenRoyaltyInfo[_tokenId];

        if (royalty.receiver == address(0)) {
            royalty = _defaultRoyaltyInfo;
        }

        uint256 royaltyAmount = (_salePrice * royalty.royaltyFraction) / _feeDenominator();

        return (royalty.receiver, royaltyAmount);
    }

    /**
     * @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
     * fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
     * override.
     */
    function _feeDenominator() internal pure virtual returns (uint96) {
        return 10000;
    }

    /**
     * @dev Sets the royalty information that all ids in this contract will default to.
     *
     * Requirements:
     *
     * - `receiver` cannot be the zero address.
     * - `feeNumerator` cannot be greater than the fee denominator.
     */
    function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
        require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
        require(receiver != address(0), "ERC2981: invalid receiver");

        _defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
    }

    /**
     * @dev Removes default royalty information.
     */
    function _deleteDefaultRoyalty() internal virtual {
        delete _defaultRoyaltyInfo;
    }

    /**
     * @dev Sets the royalty information for a specific token id, overriding the global default.
     *
     * Requirements:
     *
     * - `receiver` cannot be the zero address.
     * - `feeNumerator` cannot be greater than the fee denominator.
     */
    function _setTokenRoyalty(
        uint256 tokenId,
        address receiver,
        uint96 feeNumerator
    ) internal virtual {
        require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
        require(receiver != address(0), "ERC2981: Invalid parameters");

        _tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
    }

    /**
     * @dev Resets royalty information for the token id back to the global default.
     */
    function _resetTokenRoyalty(uint256 tokenId) internal virtual {
        delete _tokenRoyaltyInfo[tokenId];
    }
}

File 8 of 27 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

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

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

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

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

File 9 of 27 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

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

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

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

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

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

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

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

pragma solidity ^0.8.0;

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

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

pragma solidity ^0.8.0;

import "../IERC721.sol";

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

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

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

File 12 of 27 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

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

pragma solidity ^0.8.0;

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

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

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

pragma solidity ^0.8.0;

import "./IERC165.sol";

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

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

pragma solidity ^0.8.0;

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

File 16 of 27 : Create3.sol
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.4;

/**
  @title A library for deploying contracts EIP-3171 style.
  @author Agustin Aguilar <[email protected]>
*/
library Create3 {
  error ErrorCreatingProxy();
  error ErrorCreatingContract();
  error TargetAlreadyExists();

  /**
    @notice The bytecode for a contract that proxies the creation of another contract
    @dev If this code is deployed using CREATE2 it can be used to decouple `creationCode` from the child contract address
  0x67363d3d37363d34f03d5260086018f3:
      0x00  0x67  0x67XXXXXXXXXXXXXXXX  PUSH8 bytecode  0x363d3d37363d34f0
      0x01  0x3d  0x3d                  RETURNDATASIZE  0 0x363d3d37363d34f0
      0x02  0x52  0x52                  MSTORE
      0x03  0x60  0x6008                PUSH1 08        8
      0x04  0x60  0x6018                PUSH1 18        24 8
      0x05  0xf3  0xf3                  RETURN
  0x363d3d37363d34f0:
      0x00  0x36  0x36                  CALLDATASIZE    cds
      0x01  0x3d  0x3d                  RETURNDATASIZE  0 cds
      0x02  0x3d  0x3d                  RETURNDATASIZE  0 0 cds
      0x03  0x37  0x37                  CALLDATACOPY
      0x04  0x36  0x36                  CALLDATASIZE    cds
      0x05  0x3d  0x3d                  RETURNDATASIZE  0 cds
      0x06  0x34  0x34                  CALLVALUE       val 0 cds
      0x07  0xf0  0xf0                  CREATE          addr
  */

  bytes internal constant PROXY_CHILD_BYTECODE =
    hex'67_36_3d_3d_37_36_3d_34_f0_3d_52_60_08_60_18_f3';

  //                        KECCAK256_PROXY_CHILD_BYTECODE = keccak256(PROXY_CHILD_BYTECODE);
  bytes32 internal constant KECCAK256_PROXY_CHILD_BYTECODE =
    0x21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f;

  /**
    @notice Returns the size of the code on a given address
    @param _addr Address that may or may not contain code
    @return size of the code on the given `_addr`
  */
  function codeSize(address _addr) internal view returns (uint256 size) {
    assembly {
      size := extcodesize(_addr)
    }
  }

  /**
    @notice Creates a new contract with given `_creationCode` and `_salt`
    @param _salt Salt of the contract creation, resulting address will be derivated from this value only
    @param _creationCode Creation code (constructor) of the contract to be deployed, this value doesn't affect the resulting address
    @return addr of the deployed contract, reverts on error
  */
  function create3(bytes32 _salt, bytes memory _creationCode)
    internal
    returns (address addr)
  {
    return create3(_salt, _creationCode, 0);
  }

  /**
    @notice Creates a new contract with given `_creationCode` and `_salt`
    @param _salt Salt of the contract creation, resulting address will be derivated from this value only
    @param _creationCode Creation code (constructor) of the contract to be deployed, this value doesn't affect the resulting address
    @param _value In WEI of ETH to be forwarded to child contract
    @return addr of the deployed contract, reverts on error
  */
  function create3(
    bytes32 _salt,
    bytes memory _creationCode,
    uint256 _value
  ) internal returns (address addr) {
    // Creation code
    bytes memory creationCode = PROXY_CHILD_BYTECODE;

    // Get target final address
    addr = addressOf(_salt);
    if (codeSize(addr) != 0) revert TargetAlreadyExists();

    // Create CREATE2 proxy
    address proxy;
    assembly {
      proxy := create2(0, add(creationCode, 32), mload(creationCode), _salt)
    }
    if (proxy == address(0)) revert ErrorCreatingProxy();

    // Call proxy with final init code
    (bool success, ) = proxy.call{value: _value}(_creationCode);
    if (!success || codeSize(addr) == 0) revert ErrorCreatingContract();
  }

  /**
    @notice Computes the resulting address of a contract deployed using address(this) and the given `_salt`
    @param _salt Salt of the contract creation, resulting address will be derivated from this value only
    @return addr of the deployed contract, reverts on error
    @dev The address creation formula is: keccak256(rlp([keccak256(0xff ++ address(this) ++ _salt ++ keccak256(childBytecode))[12:], 0x01]))
  */
  function addressOf(bytes32 _salt) internal view returns (address) {
    address proxy = address(
      uint160(
        uint256(
          keccak256(
            abi.encodePacked(
              hex'ff',
              address(this),
              _salt,
              KECCAK256_PROXY_CHILD_BYTECODE
            )
          )
        )
      )
    );

    return
      address(
        uint160(
          uint256(keccak256(abi.encodePacked(hex'd6_94', proxy, hex'01')))
        )
      );
  }
}

File 17 of 27 : Bytecode.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

library Bytecode {
  error InvalidCodeAtRange(uint256 _size, uint256 _start, uint256 _end);

  /**
    @notice Generate a creation code that results on a contract with `_code` as bytecode
    @param _code The returning value of the resulting `creationCode`
    @return creationCode (constructor) for new contract
  */
  function creationCodeFor(bytes memory _code)
    internal
    pure
    returns (bytes memory)
  {
    /*
      0x00    0x63         0x63XXXXXX  PUSH4 _code.length  size
      0x01    0x80         0x80        DUP1                size size
      0x02    0x60         0x600e      PUSH1 14            14 size size
      0x03    0x60         0x6000      PUSH1 00            0 14 size size
      0x04    0x39         0x39        CODECOPY            size
      0x05    0x60         0x6000      PUSH1 00            0 size
      0x06    0xf3         0xf3        RETURN
      <CODE>
    */

    return
      abi.encodePacked(
        hex'63',
        uint32(_code.length),
        hex'80_60_0E_60_00_39_60_00_F3',
        _code
      );
  }

  /**
    @notice Returns the size of the code on a given address
    @param _addr Address that may or may not contain code
    @return size of the code on the given `_addr`
  */
  function codeSize(address _addr) internal view returns (uint256 size) {
    assembly {
      size := extcodesize(_addr)
    }
  }

  /**
    @notice Returns the code of a given address
    @dev It will fail if `_end < _start`
    @param _addr Address that may or may not contain code
    @param _start number of bytes of code to skip on read
    @param _end index before which to end extraction
    @return oCode read from `_addr` deployed bytecode
    Forked from: https://gist.github.com/KardanovIR/fe98661df9338c842b4a30306d507fbd
  */
  function codeAt(
    address _addr,
    uint256 _start,
    uint256 _end
  ) internal view returns (bytes memory oCode) {
    uint256 csize = codeSize(_addr);
    if (csize == 0) return bytes('');

    if (_start > csize) return bytes('');
    if (_end < _start) revert InvalidCodeAtRange(csize, _start, _end);

    unchecked {
      uint256 reqSize = _end - _start;
      uint256 maxSize = csize - _start;

      uint256 size = maxSize < reqSize ? maxSize : reqSize;

      assembly {
        // allocate output byte array - this could also be done without assembly
        // by using o_code = new bytes(size)
        oCode := mload(0x40)
        // new "memory end" including padding
        mstore(0x40, add(oCode, and(add(add(size, 0x20), 0x1f), not(0x1f))))
        // store length in memory
        mstore(oCode, size)
        // actually retrieve the code, this needs assembly
        extcodecopy(_addr, add(oCode, 0x20), _start, size)
      }
    }
  }
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`.
        // We also know that `k`, the position of the most significant bit, is such that `msb(a) = 2**k`.
        // This gives `2**k < a <= 2**(k+1)` → `2**(k/2) <= sqrt(a) < 2 ** (k/2+1)`.
        // Using an algorithm similar to the msb conmputation, we are able to compute `result = 2**(k/2)` which is a
        // good first aproximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1;
        uint256 x = a;
        if (x >> 128 > 0) {
            x >>= 128;
            result <<= 64;
        }
        if (x >> 64 > 0) {
            x >>= 64;
            result <<= 32;
        }
        if (x >> 32 > 0) {
            x >>= 32;
            result <<= 16;
        }
        if (x >> 16 > 0) {
            x >>= 16;
            result <<= 8;
        }
        if (x >> 8 > 0) {
            x >>= 8;
            result <<= 4;
        }
        if (x >> 4 > 0) {
            x >>= 4;
            result <<= 2;
        }
        if (x >> 2 > 0) {
            result <<= 1;
        }

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

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

File 19 of 27 : SafeMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol)

pragma solidity ^0.8.0;

// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
 * now has built in overflow checking.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        return a * b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b <= a, errorMessage);
            return a - b;
        }
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a / b;
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
}

File 20 of 27 : RendererPropsStorage.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;
import './libraries/SSTORE2Map.sol';

import '@openzeppelin/contracts/utils/Strings.sol';
import '@openzeppelin/contracts/utils/Base64.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import '@abf-monorepo/protocol/contracts/renderers/LayerCompositeRenderer.sol';
import '@abf-monorepo/protocol/contracts/libraries/BytesUtils.sol';

contract RendererPropsStorage is Ownable {
  uint256 public constant MAX_UINT_16 = 0xFFFF;

  // index starts from zero, useful to use the 0th index as a empty case.
  uint16 public currentMaxRendererPropsIndex = 0;

  constructor() {}

  function batchAddRendererProps(bytes[] calldata rendererProps)
    public
    onlyOwner
  {
    for (uint16 i = 0; i < rendererProps.length; ++i) {
      SSTORE2Map.write(
        bytes32(uint256(currentMaxRendererPropsIndex + i)),
        rendererProps[i]
      );
    }
    currentMaxRendererPropsIndex += uint16(rendererProps.length);
    require(
      currentMaxRendererPropsIndex <= MAX_UINT_16,
      'RendererPropsStorage: Exceeds storage limit'
    );
  }

  function indexToRendererProps(uint16 index)
    public
    view
    returns (bytes memory)
  {
    return SSTORE2Map.read(bytes32(uint256(index)));
  }
}

File 21 of 27 : ChunkedDataStorage.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;
import './libraries/SSTORE2Map.sol';

import '@openzeppelin/contracts/utils/Strings.sol';
import '@openzeppelin/contracts/utils/Base64.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import '@abf-monorepo/protocol/contracts/libraries/BytesUtils.sol';

contract ChunkedDataStorage is Ownable {
  uint256 public constant MAX_UINT_16 = 0xFFFF;

  mapping(uint256 => uint256) public numLayerDataInChunk;

  uint256 public currentMaxChunksIndex = 0;

  constructor() {}

  function batchAddChunkedData(bytes[] calldata data) public onlyOwner {
    numLayerDataInChunk[currentMaxChunksIndex] = data.length;

    bytes memory chunkedLayerData = '';

    for (uint256 i = 0; i < data.length; ++i) {
      require(
        data[i].length <= MAX_UINT_16,
        'ChunkedDataStorage: data exceeds size of 0xFFFF'
      );
      chunkedLayerData = abi.encodePacked(
        chunkedLayerData,
        uint16(data[i].length),
        data[i]
      );
    }
    SSTORE2Map.write(bytes32(currentMaxChunksIndex), chunkedLayerData);
    currentMaxChunksIndex++;
  }

  function indexToData(uint256 index) public view returns (bytes memory) {
    uint256 currentChunkIndex = 0;
    uint256 currentIndex = 0;
    do {
      currentIndex += numLayerDataInChunk[currentChunkIndex];
      currentChunkIndex++;
      if (numLayerDataInChunk[currentChunkIndex] == 0) {
        break;
      }
    } while (currentIndex <= index);
    currentChunkIndex--;
    currentIndex -= numLayerDataInChunk[currentChunkIndex];
    uint256 localChunkIndex = index - currentIndex;
    bytes memory chunkedData = SSTORE2Map.read(bytes32(currentChunkIndex));
    uint256 localChunkIndexPointer = 0;
    for (uint256 i = 0; i < chunkedData.length; i += 0) {
      if (localChunkIndexPointer == localChunkIndex) {
        return
          BytesUtils.slice(
            chunkedData,
            i + 2,
            BytesUtils.toUint16(chunkedData, i)
          );
      }
      i += BytesUtils.toUint16(chunkedData, i) + 2;
      localChunkIndexPointer++;
    }

    return '';
  }
}

File 22 of 27 : Base64.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Base64.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides a set of functions to operate with Base64 strings.
 *
 * _Available since v4.5._
 */
library Base64 {
    /**
     * @dev Base64 Encoding/Decoding Table
     */
    string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    /**
     * @dev Converts a `bytes` to its Bytes64 `string` representation.
     */
    function encode(bytes memory data) internal pure returns (string memory) {
        /**
         * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
         * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
         */
        if (data.length == 0) return "";

        // Loads the table into memory
        string memory table = _TABLE;

        // Encoding takes 3 bytes chunks of binary data from `bytes` data parameter
        // and split into 4 numbers of 6 bits.
        // The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up
        // - `data.length + 2`  -> Round up
        // - `/ 3`              -> Number of 3-bytes chunks
        // - `4 *`              -> 4 characters for each chunk
        string memory result = new string(4 * ((data.length + 2) / 3));

        /// @solidity memory-safe-assembly
        assembly {
            // Prepare the lookup table (skip the first "length" byte)
            let tablePtr := add(table, 1)

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

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

            } {
                // Advance 3 bytes
                dataPtr := add(dataPtr, 3)
                let input := mload(dataPtr)

                // To write each character, shift the 3 bytes (18 bits) chunk
                // 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
                // and apply logical AND with 0x3F which is the number of
                // the previous character in the ASCII table prior to the Base64 Table
                // The result is then added to the table to get the character to write,
                // and finally write it in the result pointer but with a left shift
                // of 256 (1 byte) - 8 (1 ASCII char) = 248 bits

                mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance
            }

            // When data `bytes` is not exactly 3 bytes long
            // it is padded with `=` characters at the end
            switch mod(mload(data), 3)
            case 1 {
                mstore8(sub(resultPtr, 1), 0x3d)
                mstore8(sub(resultPtr, 2), 0x3d)
            }
            case 2 {
                mstore8(sub(resultPtr, 1), 0x3d)
            }
        }

        return result;
    }
}

File 23 of 27 : LayerCompositeRenderer.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;
import "../interfaces/IRenderer.sol";
import "../libraries/BytesUtils.sol";
import "../libraries/SvgUtils.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import '@openzeppelin/contracts/utils/introspection/ERC165.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import "@openzeppelin/contracts/utils/Base64.sol";

contract LayerCompositeRenderer is IRenderer, Ownable, ERC165 {
  using Strings for uint256;

  function owner() public override(Ownable, IRenderer) view returns (address) {
    return super.owner();
  }

  function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
    return
      interfaceId == type(IRenderer).interfaceId ||
      super.supportsInterface(interfaceId);
  }

  function propsSize() external override pure returns (uint256) {
    return 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
  }
  function additionalMetadataURI() external override pure returns (string memory) {
    return "ipfs://bafkreigjwztwrolwcbkbz3ombzkvxg2767bckeobrfwdjfohvxgozbepv4";
  }
  
  function renderAttributeKey() external override pure returns (string memory) {
    return "image";
  }
  
  function name() public override pure returns (string memory) {
    return 'Layer Composite';
  }

  function encodeProps(address[] memory renderers, bytes[] memory rendererProps) public pure returns (bytes memory output) {
    for (uint i = 0; i < renderers.length; ++i) {
      output = abi.encodePacked(output, renderers[i], rendererProps[i].length, rendererProps[i]);
    }
  }

  function renderRaw(bytes calldata props) public override view returns (bytes memory) {
    bytes memory backgroundImages;

    for (uint i = 0; i < props.length; i += 0) {
      IRenderer destinationRenderer = IRenderer(BytesUtils.toAddress(props, i));
      uint start = i + 20 + 32;
      uint end = start + BytesUtils.toUint256(props, i + 20); 
      backgroundImages = abi.encodePacked(backgroundImages, i == 0  ? '' : ',', 'url(', 
      destinationRenderer.render(props[start:end])
      ,')');
      i = end;
    }

    return abi.encodePacked(
      '<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1200" style="',
      'background-image:', backgroundImages, ';background-repeat:no-repeat;background-size:contain;background-position:center;image-rendering:-webkit-optimize-contrast;-ms-interpolation-mode:nearest-neighbor;image-rendering:-moz-crisp-edges;image-rendering:pixelated;">',
      '</svg>'
    );
  }

  function render(bytes calldata props) external override view returns (string memory) {
        return string(
      abi.encodePacked(
        'data:image/svg+xml;base64,',
        Base64.encode(renderRaw(props)) 
      )
    );
  }

  function attributes(bytes calldata) external override pure returns (string memory) {
    return ""; 
  }
}

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

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

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

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

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

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

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

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

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

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

        return tempBytes;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        bytes memory tempBytes;

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

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

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

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

                mstore(tempBytes, _length)

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

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

        return tempBytes;
    }

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

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

        return tempAddress;
    }

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

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

        return tempUint;
    }

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

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

        return tempUint;
    }

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

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

        return tempUint;
    }

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

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

        return tempUint;
    }

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

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

        return tempUint;
    }

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

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

        return tempUint;
    }

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

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

        return tempUint;
    }

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

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

        return tempBytes32;
    }

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

        assembly {
            let length := mload(_preBytes)

            // if lengths don't match the arrays are not equal
            switch eq(length, mload(_postBytes))
            case 1 {
                // cb is a circuit breaker in the for loop since there's
                //  no said feature for inline assembly loops
                // cb = 1 - don't breaker
                // cb = 0 - break
                let cb := 1

                let mc := add(_preBytes, 0x20)
                let end := add(mc, length)

                for {
                    let cc := add(_postBytes, 0x20)
                // the next line is the loop condition:
                // while(uint256(mc < end) + cb == 2)
                } eq(add(lt(mc, end), cb), 2) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    // if any of these checks fails then arrays are not equal
                    if iszero(eq(mload(mc), mload(cc))) {
                        // unsuccess:
                        success := 0
                        cb := 0
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }

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

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

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

                        if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
                            // unsuccess:
                            success := 0
                        }
                    }
                    default {
                        // cb is a circuit breaker in the for loop since there's
                        //  no said feature for inline assembly loops
                        // cb = 1 - don't breaker
                        // cb = 0 - break
                        let cb := 1

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

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

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

        return success;
    }
}

File 25 of 27 : IRenderer.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

import '@openzeppelin/contracts/utils/introspection/IERC165.sol';

interface IRenderer is IERC165 {
  function name() external view returns (string memory);
  function owner() external view returns (address);
  function propsSize() external view returns (uint256);
  function additionalMetadataURI() external view returns (string memory);
  function renderAttributeKey() external view returns (string memory);
  function renderRaw(bytes calldata props) external view returns (bytes memory);
  function render(bytes calldata props) external view returns (string memory);
  function attributes(bytes calldata props) external view returns (string memory);
}

File 26 of 27 : SvgUtils.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;
import "@openzeppelin/contracts/utils/Strings.sol";

library SvgUtils {
  using Strings for uint256;

  uint public constant DECIMALS = 4;
  uint public constant ONE_UNIT = 10 ** DECIMALS;

  function padZeros(string memory s, uint len) public pure returns (string memory) {
    uint local_len = bytes(s).length;
    string memory local_s = s;
    while(local_len < len) {
      local_s = string(abi.encodePacked('0', local_s));
      local_len++;
    }
    return local_s;
  }

  function wholeNumber(uint n) public pure returns (uint) {
    return n / ONE_UNIT;
  }

  function decimals(uint n) public pure returns (uint) {
    return n % ONE_UNIT;
  }

  function toDecimalString(uint n) public pure returns (string memory s) {
    if (n == 0) return '0';

    s = string(abi.encodePacked(
      (n / (ONE_UNIT)).toString(), '.' , padZeros((n % ONE_UNIT).toString(), DECIMALS)
    ));
  }

  function lerpWithDecimals(uint min, uint max, bytes1 scale) public pure returns (uint) {
    if (scale == 0x0) return min * ONE_UNIT;
    if (scale == 0xff) return max * ONE_UNIT;
    uint delta = ((max - min) * ONE_UNIT * uint(uint8(scale))) / 255; 
    return (min * ONE_UNIT) + delta;
  }

  bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";

  function toColorHexString(uint256 value) public pure returns (string memory) {
      bytes memory buffer = new bytes(2 * 3 + 1);
      buffer[0] = "#";
      for (uint256 i = 2 * 3; i > 0; --i) {
          buffer[i] = _HEX_SYMBOLS[value & 0xf];
          value >>= 4;
      }
      require(value == 0, "Strings: hex length insufficient");
      return string(buffer);
  }

  function toColorHexStringByBytes(bytes1 r, bytes1 g, bytes1 b) public pure returns (string memory) {
    bytes memory buffer = new bytes(7);
    buffer[0] = "#";
    buffer[2] = _HEX_SYMBOLS[uint8(r) & 0xf];
    r >>= 4;
    buffer[1] = _HEX_SYMBOLS[uint8(r) & 0xf];
    buffer[4] = _HEX_SYMBOLS[uint8(g) & 0xf];
    g >>= 4;
    buffer[3] = _HEX_SYMBOLS[uint8(g) & 0xf];
    buffer[6] = _HEX_SYMBOLS[uint8(b) & 0xf];
    b >>= 4;
    buffer[5] = _HEX_SYMBOLS[uint8(b) & 0xf];
    return string(buffer);
  }
  
  function toColorHexStringByBytes3(bytes3 rgb) public pure returns (string memory) {
    return toColorHexStringByBytes(rgb[0], rgb[1], rgb[2]);
  }

}

File 27 of 27 : IERC2981.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)

pragma solidity ^0.8.0;

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

/**
 * @dev Interface for the NFT Royalty Standard.
 *
 * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
 * support for royalty payments across all NFT marketplaces and ecosystem participants.
 *
 * _Available since v4.5._
 */
interface IERC2981 is IERC165 {
    /**
     * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
     * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
     */
    function royaltyInfo(uint256 tokenId, uint256 salePrice)
        external
        view
        returns (address receiver, uint256 royaltyAmount);
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"address","name":"treasury","type":"address"},{"internalType":"address","name":"boostToken","type":"address"},{"internalType":"address","name":"rce","type":"address"},{"internalType":"address","name":"curve","type":"address"},{"internalType":"uint256","name":"a0","type":"uint256"},{"internalType":"uint256","name":"b0","type":"uint256"},{"internalType":"uint256","name":"c0","type":"uint256"},{"internalType":"uint256","name":"d0","type":"uint256"},{"internalType":"uint256","name":"a1","type":"uint256"},{"internalType":"uint256","name":"b1","type":"uint256"},{"internalType":"uint256","name":"c1","type":"uint256"},{"internalType":"uint256","name":"d1","type":"uint256"}],"internalType":"struct Merge.DeployMergeNFTConfig","name":"config","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"ApprovalQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"ApprovalToCurrentOwner","type":"error"},{"inputs":[],"name":"ApproveToCaller","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[],"name":"OwnerQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"TransferCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"shutdown","type":"bool"}],"name":"ChangedEmergencyShutdown","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isActive","type":"bool"}],"name":"ChangedIsActive","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"MAX_MINTING_PER_BLOCK","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"a0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"a1","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"b0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"b1","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"blacklistMap","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"blockMintingGuardMap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"boostToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"boostTokenBaseAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"c0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"c1","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contractURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"curve","outputs":[{"internalType":"contract SigmoidThreshold","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"d0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"d1","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deployDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencyShutdown","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"}],"name":"getBoostScore","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"}],"name":"getCurrentRarityScore","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getRarityScoreForToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"userAddress","type":"address"}],"name":"getTokenBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isMergeByDifficulty","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"numMints","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numSecondsSinceDeploy","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rarityTokenMap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rce","outputs":[{"internalType":"contract RarityCompositingEngine","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_list","type":"address[]"}],"name":"setBlacklist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_boostToken","type":"address"}],"name":"setBoostToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"setBoostTokenBaseAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_curve","type":"address"}],"name":"setCurve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"a0","type":"uint256"},{"internalType":"uint256","name":"b0","type":"uint256"},{"internalType":"uint256","name":"c0","type":"uint256"},{"internalType":"uint256","name":"d0","type":"uint256"},{"internalType":"uint256","name":"a1","type":"uint256"},{"internalType":"uint256","name":"b1","type":"uint256"},{"internalType":"uint256","name":"c1","type":"uint256"},{"internalType":"uint256","name":"d1","type":"uint256"}],"internalType":"struct Merge.SetCurveParams","name":"config","type":"tuple"}],"name":"setCurveParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_date","type":"uint256"}],"name":"setDeployDate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"shutdown","type":"bool"}],"name":"setEmergencyShutdown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isActive","type":"bool"}],"name":"setIsActive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_max","type":"uint256"}],"name":"setMaxMinting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rce","type":"address"}],"name":"setRCE","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint96","name":"newRoyaltyFraction","type":"uint96"}],"name":"setRoyalty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_treasury","type":"address"}],"name":"setTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

60806040526003600b556103e86019556000601b60006101000a81548160ff0219169083151502179055503480156200003757600080fd5b50604051620065173803806200651783398181016040528101906200005d9190620007c4565b6200006d6200024d60201b60201c565b60008190555062000093620000876200025260201b60201c565b6200025a60201b60201c565b806000015160029080519060200190620000af92919062000320565b50806020015160039080519060200190620000cc92919062000320565b508060c00151600e819055508060e00151600f81905550806101000151601081905550806101200151601181905550806101400151601281905550806101600151601381905550806101800151601481905550806101a001516015819055508060600151601760006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060a00151600d60016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555042600c819055508060400151601660006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060800151601860006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506200087a565b600090565b600033905090565b6000600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600a60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b8280546200032e9062000844565b90600052602060002090601f0160209004810192826200035257600085556200039e565b82601f106200036d57805160ff19168380011785556200039e565b828001600101855582156200039e579182015b828111156200039d57825182559160200191906001019062000380565b5b509050620003ad9190620003b1565b5090565b5b80821115620003cc576000816000905550600101620003b2565b5090565b6000604051905090565b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6200043482620003e9565b810181811067ffffffffffffffff82111715620004565762000455620003fa565b5b80604052505050565b60006200046b620003d0565b905062000479828262000429565b919050565b600080fd5b600080fd5b600080fd5b600067ffffffffffffffff821115620004ab57620004aa620003fa565b5b620004b682620003e9565b9050602081019050919050565b60005b83811015620004e3578082015181840152602081019050620004c6565b83811115620004f3576000848401525b50505050565b6000620005106200050a846200048d565b6200045f565b9050828152602081018484840111156200052f576200052e62000488565b5b6200053c848285620004c3565b509392505050565b600082601f8301126200055c576200055b62000483565b5b81516200056e848260208601620004f9565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620005a48262000577565b9050919050565b620005b68162000597565b8114620005c257600080fd5b50565b600081519050620005d681620005ab565b92915050565b6000819050919050565b620005f181620005dc565b8114620005fd57600080fd5b50565b6000815190506200061181620005e6565b92915050565b60006101c08284031215620006315762000630620003e4565b5b6200063e6101c06200045f565b9050600082015167ffffffffffffffff8111156200066157620006606200047e565b5b6200066f8482850162000544565b600083015250602082015167ffffffffffffffff8111156200069657620006956200047e565b5b620006a48482850162000544565b6020830152506040620006ba84828501620005c5565b6040830152506060620006d084828501620005c5565b6060830152506080620006e684828501620005c5565b60808301525060a0620006fc84828501620005c5565b60a08301525060c0620007128482850162000600565b60c08301525060e0620007288482850162000600565b60e0830152506101006200073f8482850162000600565b61010083015250610120620007578482850162000600565b610120830152506101406200076f8482850162000600565b61014083015250610160620007878482850162000600565b610160830152506101806200079f8482850162000600565b610180830152506101a0620007b78482850162000600565b6101a08301525092915050565b600060208284031215620007dd57620007dc620003da565b5b600082015167ffffffffffffffff811115620007fe57620007fd620003df565b5b6200080c8482850162000617565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200085d57607f821691505b6020821081141562000874576200087362000815565b5b50919050565b615c8d806200088a6000396000f3fe6080604052600436106103965760003560e01c8063715018a6116101dc578063c489744b11610102578063e7eba1ee116100a0578063ee4950021161006f578063ee49500214610dbd578063f0f4426014610de8578063f2fde38b14610e11578063ff31f71b14610e3a57610396565b8063e7eba1ee14610ced578063e8a3d48514610d2a578063e985e9c514610d55578063eb91d37e14610d9257610396565b8063c87b56dd116100dc578063c87b56dd14610c35578063c8f05d2b14610c72578063cac9266914610c9b578063cd2d9af914610cc457610396565b8063c489744b14610ba4578063c65f543214610be1578063c86f1a0214610c0c57610396565b80639aa0ea191161017a578063ae5e97d311610149578063ae5e97d314610aec578063b88d4fde14610b29578063bf77249114610b52578063c3e29cfc14610b7b57610396565b80639aa0ea1914610a1e578063a030695014610a5b578063a22cb46514610a98578063a9874b2a14610ac157610396565b80638a162b9c116101b65780638a162b9c146109605780638c18f2f11461099d5780638da5cb5b146109c857806395d89b41146109f357610396565b8063715018a6146108f35780637165485d1461090a578063763c07881461093557610396565b80633602175c116102c15780635f57697c1161025f5780636352211e1161022e5780636352211e14610823578063656bb5651461086057806369668fe91461088b57806370a08231146108b657610396565b80635f57697c1461077957806361d027b3146107a45780636224bcfd146107cf57806362b9cc13146107fa57610396565b806342842e0e1161029b57806342842e0e146106d357806348dc9d2b146106fc578063576f71391461072557806358258c2b1461074e57610396565b80633602175c146106615780633a589b971461068c57806340c10f19146106b757610396565b806314c644021161033957806326987b601161030857806326987b60146105a45780632750fc78146105cf5780632a55205a146105f85780633403c2fc1461063657610396565b806314c64402146104fc57806318160ddd1461052557806322f3e2d41461055057806323b872dd1461057b57610396565b8063081812fc11610375578063081812fc14610440578063095ea7b31461047d578063119552a1146104a657806312a39614146104d157610396565b8062b7d5e11461039b57806301ffc9a7146103d857806306fdde0314610415575b600080fd5b3480156103a757600080fd5b506103c260048036038101906103bd9190613d75565b610e65565b6040516103cf9190613db1565b60405180910390f35b3480156103e457600080fd5b506103ff60048036038101906103fa9190613e24565b610e7d565b60405161040c9190613e6c565b60405180910390f35b34801561042157600080fd5b5061042a610e8f565b6040516104379190613f20565b60405180910390f35b34801561044c57600080fd5b5061046760048036038101906104629190613d75565b610f21565b6040516104749190613f83565b60405180910390f35b34801561048957600080fd5b506104a4600480360381019061049f9190613fca565b610f9d565b005b3480156104b257600080fd5b506104bb6110a8565b6040516104c89190613db1565b60405180910390f35b3480156104dd57600080fd5b506104e66110ae565b6040516104f39190613db1565b60405180910390f35b34801561050857600080fd5b50610523600480360381019061051e9190614036565b6110b4565b005b34801561053157600080fd5b5061053a611110565b6040516105479190613db1565b60405180910390f35b34801561055c57600080fd5b50610565611127565b6040516105729190613e6c565b60405180910390f35b34801561058757600080fd5b506105a2600480360381019061059d9190614063565b61113a565b005b3480156105b057600080fd5b506105b961114a565b6040516105c69190613db1565b60405180910390f35b3480156105db57600080fd5b506105f660048036038101906105f19190614036565b611153565b005b34801561060457600080fd5b5061061f600480360381019061061a91906140b6565b6111be565b60405161062d9291906140f6565b60405180910390f35b34801561064257600080fd5b5061064b6113a9565b6040516106589190613e6c565b60405180910390f35b34801561066d57600080fd5b506106766113bc565b6040516106839190613db1565b60405180910390f35b34801561069857600080fd5b506106a16113c2565b6040516106ae9190613f83565b60405180910390f35b6106d160048036038101906106cc9190613fca565b6113e8565b005b3480156106df57600080fd5b506106fa60048036038101906106f59190614063565b6117a2565b005b34801561070857600080fd5b50610723600480360381019061071e919061411f565b6117c2565b005b34801561073157600080fd5b5061074c60048036038101906107479190613d75565b61180e565b005b34801561075a57600080fd5b50610763611820565b6040516107709190613e6c565b60405180910390f35b34801561078557600080fd5b5061078e61183e565b60405161079b9190613db1565b60405180910390f35b3480156107b057600080fd5b506107b9611844565b6040516107c69190613f83565b60405180910390f35b3480156107db57600080fd5b506107e461186a565b6040516107f191906141ab565b60405180910390f35b34801561080657600080fd5b50610821600480360381019061081c919061411f565b611890565b005b34801561082f57600080fd5b5061084a60048036038101906108459190613d75565b6118dc565b6040516108579190613f83565b60405180910390f35b34801561086c57600080fd5b506108756118f2565b6040516108829190613db1565b60405180910390f35b34801561089757600080fd5b506108a06118f8565b6040516108ad9190613db1565b60405180910390f35b3480156108c257600080fd5b506108dd60048036038101906108d8919061411f565b6118fe565b6040516108ea9190613db1565b60405180910390f35b3480156108ff57600080fd5b506109086119ce565b005b34801561091657600080fd5b5061091f6119e2565b60405161092c91906141e7565b60405180910390f35b34801561094157600080fd5b5061094a611a08565b6040516109579190613db1565b60405180910390f35b34801561096c57600080fd5b506109876004803603810190610982919061411f565b611a1d565b6040516109949190613db1565b60405180910390f35b3480156109a957600080fd5b506109b2611bbe565b6040516109bf9190613db1565b60405180910390f35b3480156109d457600080fd5b506109dd611bc4565b6040516109ea9190613f83565b60405180910390f35b3480156109ff57600080fd5b50610a08611bee565b604051610a159190613f20565b60405180910390f35b348015610a2a57600080fd5b50610a456004803603810190610a40919061411f565b611c80565b604051610a529190613e6c565b60405180910390f35b348015610a6757600080fd5b50610a826004803603810190610a7d919061411f565b611ca0565b604051610a8f9190613db1565b60405180910390f35b348015610aa457600080fd5b50610abf6004803603810190610aba9190614202565b611cfe565b005b348015610acd57600080fd5b50610ad6611e76565b604051610ae39190613db1565b60405180910390f35b348015610af857600080fd5b50610b136004803603810190610b0e9190613d75565b611e7c565b604051610b209190613db1565b60405180910390f35b348015610b3557600080fd5b50610b506004803603810190610b4b9190614377565b611f29565b005b348015610b5e57600080fd5b50610b796004803603810190610b7491906144c2565b611fa5565b005b348015610b8757600080fd5b50610ba26004803603810190610b9d91906145da565b612040565b005b348015610bb057600080fd5b50610bcb6004803603810190610bc69190614608565b61208d565b604051610bd89190613db1565b60405180910390f35b348015610bed57600080fd5b50610bf6612120565b604051610c039190613db1565b60405180910390f35b348015610c1857600080fd5b50610c336004803603810190610c2e9190613d75565b612126565b005b348015610c4157600080fd5b50610c5c6004803603810190610c579190613d75565b612138565b604051610c699190613f20565b60405180910390f35b348015610c7e57600080fd5b50610c996004803603810190610c949190613d75565b61242d565b005b348015610ca757600080fd5b50610cc26004803603810190610cbd919061468c565b61243f565b005b348015610cd057600080fd5b50610ceb6004803603810190610ce6919061411f565b612476565b005b348015610cf957600080fd5b50610d146004803603810190610d0f91906146ef565b6124c2565b604051610d219190613db1565b60405180910390f35b348015610d3657600080fd5b50610d3f6124da565b604051610d4c9190613f20565b60405180910390f35b348015610d6157600080fd5b50610d7c6004803603810190610d779190614608565b612529565b604051610d899190613e6c565b60405180910390f35b348015610d9e57600080fd5b50610da76125bd565b604051610db49190613db1565b60405180910390f35b348015610dc957600080fd5b50610dd26126bf565b604051610ddf9190613db1565b60405180910390f35b348015610df457600080fd5b50610e0f6004803603810190610e0a919061411f565b6126c5565b005b348015610e1d57600080fd5b50610e386004803603810190610e33919061411f565b612711565b005b348015610e4657600080fd5b50610e4f612795565b604051610e5c9190613db1565b60405180910390f35b601a6020528060005260406000206000915090505481565b6000610e888261279b565b9050919050565b606060028054610e9e9061474b565b80601f0160208091040260200160405190810160405280929190818152602001828054610eca9061474b565b8015610f175780601f10610eec57610100808354040283529160200191610f17565b820191906000526020600020905b815481529060010190602001808311610efa57829003601f168201915b5050505050905090565b6000610f2c82612815565b610f62576040517fcf4700e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6006600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b6000610fa8826118dc565b90508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415611010576040517f943f7b8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1661102f612863565b73ffffffffffffffffffffffffffffffffffffffff1614158015611061575061105f8161105a612863565b612529565b155b15611098576040517fcfb3b94200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110a383838361286b565b505050565b60125481565b600b5481565b6110bc61291d565b80601b60006101000a81548160ff0219169083151502179055507fb28c3d380b31a9985118fb02f15e73682e9338a972a19723b46ac25b4feb9dc0816040516111059190613e6c565b60405180910390a150565b600061111a61299b565b6001546000540303905090565b600d60009054906101000a900460ff1681565b6111458383836129a0565b505050565b60008054905090565b61115b61291d565b80600d60006101000a81548160ff0219169083151502179055507fa087c8b791d7a9eaf08393fedb4bc4f0e697ebc3f2538f7184b505925a32489f600d60009054906101000a900460ff166040516111b39190613e6c565b60405180910390a150565b6000806000600960008681526020019081526020016000206040518060400160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016000820160149054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff166bffffffffffffffffffffffff16815250509050600073ffffffffffffffffffffffffffffffffffffffff16816000015173ffffffffffffffffffffffffffffffffffffffff1614156113545760086040518060400160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016000820160149054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff166bffffffffffffffffffffffff168152505090505b600061135e612e56565b6bffffffffffffffffffffffff1682602001516bffffffffffffffffffffffff168661138a91906147ac565b6113949190614835565b90508160000151819350935050509250929050565b601b60009054906101000a900460ff1681565b600f5481565b601760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600d60009054906101000a900460ff16611437576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161142e906148d8565b60405180910390fd5b61143f611820565b1561147f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114769061496a565b60405180910390fd5b601b60009054906101000a900460ff16156114cf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114c6906149d6565b60405180910390fd5b600043336040516020016114e49291906149f6565b604051602081830303815290604052805190602001209050600b5482601c60008481526020019081526020016000205461151e9190614a1f565b111561155f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161155690614ac1565b60405180910390fd5b601d60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156115ec576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016115e390614b2d565b60405180910390fd5b6000826115f76125bd565b61160191906147ac565b905034811115611646576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161163d90614bbf565b60405180910390fd5b600061165133611a1d565b905080601a6000805481526020019081526020016000208190555083601c60008581526020019081526020016000205461168b9190614a1f565b601c6000858152602001908152602001600020819055506116be8585604051806020016040528060008152506000612e60565b601660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168260405161170490614c10565b60006040518083038185875af1925050503d8060008114611741576040519150601f19603f3d011682016040523d82523d6000602084013e611746565b606091505b5050503373ffffffffffffffffffffffffffffffffffffffff166108fc833461176f9190614c25565b9081150290604051600060405180830381858888f1935050505015801561179a573d6000803e3d6000fd5b505050505050565b6117bd83838360405180602001604052806000815250611f29565b505050565b6117ca61291d565b80600d60016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b61181661291d565b80600c8190555050565b6000680100000000000000004411806118395750600044145b905090565b60145481565b601660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b601860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b61189861291d565b80601860006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60006118e78261322e565b600001519050919050565b60105481565b600c5481565b60008073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611966576040517f8f4eb60400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600560008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900467ffffffffffffffff1667ffffffffffffffff169050919050565b6119d661291d565b6119e060006134bd565b565b600d60019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600c5442611a189190614c25565b905090565b6000611a27613cb9565b611a2f611a08565b8160000181815250506012548160200181815250506013548160400181815250506014548160600181815250506015548160800181815250506000600d60019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c6159199836040518263ffffffff1660e01b8152600401611ac59190614cd0565b60206040518083038186803b158015611add57600080fd5b505afa158015611af1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b159190614d00565b90503073ffffffffffffffffffffffffffffffffffffffff1663a0306950856040518263ffffffff1660e01b8152600401611b509190613f83565b60206040518083038186803b158015611b6857600080fd5b505afa925050508015611b9957506040513d601f19601f82011682018060405250810190611b969190614d00565b60015b611ba7578092505050611bb9565b8082611bb39190614a1f565b93505050505b919050565b60155481565b6000600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b606060038054611bfd9061474b565b80601f0160208091040260200160405190810160405280929190818152602001828054611c299061474b565b8015611c765780601f10611c4b57610100808354040283529160200191611c76565b820191906000526020600020905b815481529060010190602001808311611c5957829003601f168201915b5050505050905090565b601d6020528060005260406000206000915054906101000a900460ff1681565b600080611ccf601760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168461208d565b905060006010821015611ce25781611ce5565b60105b905060195481611cf591906147ac565b92505050919050565b611d06612863565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611d6b576040517fb06307db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060076000611d78612863565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff16611e25612863565b73ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3183604051611e6a9190613e6c565b60405180910390a35050565b60115481565b60008082905080611e8b61299b565b11158015611e9a575060005481105b15611ef2575b600115611ef1576000601a60008381526020019081526020016000205414611ede57601a600082815260200190815260200160002054915050611f24565b8080611ee990614d2d565b915050611ea0565b5b6040517fdf2d9b4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b919050565b611f348484846129a0565b611f538373ffffffffffffffffffffffffffffffffffffffff16613583565b8015611f685750611f66848484846135a6565b155b15611f9f576040517fd1a57ed600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b611fad61291d565b60005b815181101561203c576001601d6000848481518110611fd257611fd1614d57565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508061203590614d86565b9050611fb0565b5050565b61204861291d565b8060000151600e819055508060200151600f81905550806040015160108190555080608001516012819055508060a001516013819055508060c0015160148190555050565b60008273ffffffffffffffffffffffffffffffffffffffff166370a08231836040518263ffffffff1660e01b81526004016120c89190613f83565b60206040518083038186803b1580156120e057600080fd5b505afa1580156120f4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121189190614d00565b905092915050565b600e5481565b61212e61291d565b80600b8190555050565b606061214382612815565b612182576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161217990614e1b565b60405180910390fd5b600061218d83611e7c565b9050600081846040516020016121a4929190614e5c565b60405160208183030381529060405290506000601860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c5141cfe84846040518363ffffffff1660e01b8152600401612214929190614edd565b60006040518083038186803b15801561222c57600080fd5b505afa158015612240573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525081019061226991906150cd565b9150506000601860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663be496f0e836040518263ffffffff1660e01b81526004016122c99190615203565b60006040518083038186803b1580156122e157600080fd5b505afa1580156122f5573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525081019061231e91906152c6565b905061240361232c87613706565b82601860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166352ed580c866040518263ffffffff1660e01b81526004016123889190615203565b60006040518083038186803b1580156123a057600080fd5b505afa1580156123b4573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906123dd91906152c6565b6040516020016123ef939291906155ab565b604051602081830303815290604052613867565b6040516020016124139190615675565b604051602081830303815290604052945050505050919050565b61243561291d565b8060198190555050565b61244761291d565b612473601660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16826139cb565b50565b61247e61291d565b80601760006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b601c6020528060005260406000206000915090505481565b606061250560026040516020016124f19190615919565b604051602081830303815290604052613867565b6040516020016125159190615675565b604051602081830303815290604052905090565b6000600760008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b60006125c7613cb9565b6125cf611a08565b816000018181525050600e54816020018181525050600f548160400181815250506010548160600181815250506011548160800181815250506000600d60019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c6159199836040518263ffffffff1660e01b81526004016126659190614cd0565b60206040518083038186803b15801561267d57600080fd5b505afa158015612691573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126b59190614d00565b9050809250505090565b60135481565b6126cd61291d565b80601660006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b61271961291d565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415612789576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612780906159ce565b60405180910390fd5b612792816134bd565b50565b60195481565b60007f2a55205a000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061280e575061280d82613b61565b5b9050919050565b60008161282061299b565b1115801561282f575060005482105b801561285c575060046000838152602001908152602001600020600001601c9054906101000a900460ff16155b9050919050565b600033905090565b826006600084815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550818373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a4505050565b612925612863565b73ffffffffffffffffffffffffffffffffffffffff16612943611bc4565b73ffffffffffffffffffffffffffffffffffffffff1614612999576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161299090615a3a565b60405180910390fd5b565b600090565b60006129ab8261322e565b90508373ffffffffffffffffffffffffffffffffffffffff16816000015173ffffffffffffffffffffffffffffffffffffffff1614612a16576040517fa114810000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008473ffffffffffffffffffffffffffffffffffffffff16612a37612863565b73ffffffffffffffffffffffffffffffffffffffff161480612a665750612a6585612a60612863565b612529565b5b80612aab5750612a74612863565b73ffffffffffffffffffffffffffffffffffffffff16612a9384610f21565b73ffffffffffffffffffffffffffffffffffffffff16145b905080612ae4576040517f59c896be00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161415612b4b576040517fea553b3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612b588585856001613c43565b612b646000848761286b565b6001600560008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160008282829054906101000a900467ffffffffffffffff160392506101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506001600560008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160008282829054906101000a900467ffffffffffffffff160192506101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506000600460008581526020019081526020016000209050848160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550428160000160146101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555060006001850190506000600460008381526020019081526020016000209050600073ffffffffffffffffffffffffffffffffffffffff168160000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415612de4576000548214612de357878160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555084602001518160000160146101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055505b5b505050828473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4612e4f8585856001613c49565b5050505050565b6000612710905090565b600080549050600073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161415612ecd576040517f2e07630000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000841415612f08576040517fb562e8dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612f156000868387613c43565b83600560008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160008282829054906101000a900467ffffffffffffffff160192506101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555083600560008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160088282829054906101000a900467ffffffffffffffff160192506101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550846004600083815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550426004600083815260200190815260200160002060000160146101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506000819050600085820190508380156130df57506130de8773ffffffffffffffffffffffffffffffffffffffff16613583565b5b156131a5575b818773ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a461315460008884806001019550886135a6565b61318a576040517fd1a57ed600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b808214156130e55782600054146131a057600080fd5b613211565b5b818060010192508773ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4808214156131a6575b8160008190555050506132276000868387613c49565b5050505050565b613236613ce8565b60008290508061324461299b565b11158015613253575060005481105b15613486576000600460008381526020019081526020016000206040518060600160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016000820160149054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff16815260200160008201601c9054906101000a900460ff1615151515815250509050806040015161348457600073ffffffffffffffffffffffffffffffffffffffff16816000015173ffffffffffffffffffffffffffffffffffffffff16146133685780925050506134b8565b5b60011561348357818060019003925050600460008381526020019081526020016000206040518060600160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016000820160149054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff16815260200160008201601c9054906101000a900460ff1615151515815250509050600073ffffffffffffffffffffffffffffffffffffffff16816000015173ffffffffffffffffffffffffffffffffffffffff161461347e5780925050506134b8565b613369565b5b505b6040517fdf2d9b4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b919050565b6000600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600a60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b60008373ffffffffffffffffffffffffffffffffffffffff1663150b7a026135cc612863565b8786866040518563ffffffff1660e01b81526004016135ee9493929190615a5a565b602060405180830381600087803b15801561360857600080fd5b505af192505050801561363957506040513d601f19601f820116820180604052508101906136369190615abb565b60015b6136b3573d8060008114613669576040519150601f19603f3d011682016040523d82523d6000602084013e61366e565b606091505b506000815114156136ab576040517fd1a57ed600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805181602001fd5b63150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614915050949350505050565b6060600082141561374e576040518060400160405280600181526020017f30000000000000000000000000000000000000000000000000000000000000008152509050613862565b600082905060005b6000821461378057808061376990614d86565b915050600a826137799190614835565b9150613756565b60008167ffffffffffffffff81111561379c5761379b61424c565b5b6040519080825280601f01601f1916602001820160405280156137ce5781602001600182028036833780820191505090505b5090505b6000851461385b576001826137e79190614c25565b9150600a856137f69190615ae8565b60306138029190614a1f565b60f81b81838151811061381857613817614d57565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600a856138549190614835565b94506137d2565b8093505050505b919050565b606060008251141561388a576040518060200160405280600081525090506139c6565b6000604051806060016040528060408152602001615c1860409139905060006003600285516138b99190614a1f565b6138c39190614835565b60046138cf91906147ac565b67ffffffffffffffff8111156138e8576138e761424c565b5b6040519080825280601f01601f19166020018201604052801561391a5781602001600182028036833780820191505090505b509050600182016020820185865187015b80821015613986576003820191508151603f8160121c168501518453600184019350603f81600c1c168501518453600184019350603f8160061c168501518453600184019350603f811685015184536001840193505061392b565b50506003865106600181146139a257600281146139b5576139bd565b603d6001830353603d60028303536139bd565b603d60018303535b50505080925050505b919050565b6139d3612e56565b6bffffffffffffffffffffffff16816bffffffffffffffffffffffff161115613a31576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613a2890615b8b565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415613aa1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613a9890615bf7565b60405180910390fd5b60405180604001604052808373ffffffffffffffffffffffffffffffffffffffff168152602001826bffffffffffffffffffffffff16815250600860008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160000160146101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055509050505050565b60007f80ac58cd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480613c2c57507f5b5e139f000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b80613c3c5750613c3b82613c4f565b5b9050919050565b50505050565b50505050565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b6040518060600160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001600067ffffffffffffffff1681526020016000151581525090565b6000604051905090565b600080fd5b600080fd5b6000819050919050565b613d5281613d3f565b8114613d5d57600080fd5b50565b600081359050613d6f81613d49565b92915050565b600060208284031215613d8b57613d8a613d35565b5b6000613d9984828501613d60565b91505092915050565b613dab81613d3f565b82525050565b6000602082019050613dc66000830184613da2565b92915050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b613e0181613dcc565b8114613e0c57600080fd5b50565b600081359050613e1e81613df8565b92915050565b600060208284031215613e3a57613e39613d35565b5b6000613e4884828501613e0f565b91505092915050565b60008115159050919050565b613e6681613e51565b82525050565b6000602082019050613e816000830184613e5d565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b83811015613ec1578082015181840152602081019050613ea6565b83811115613ed0576000848401525b50505050565b6000601f19601f8301169050919050565b6000613ef282613e87565b613efc8185613e92565b9350613f0c818560208601613ea3565b613f1581613ed6565b840191505092915050565b60006020820190508181036000830152613f3a8184613ee7565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000613f6d82613f42565b9050919050565b613f7d81613f62565b82525050565b6000602082019050613f986000830184613f74565b92915050565b613fa781613f62565b8114613fb257600080fd5b50565b600081359050613fc481613f9e565b92915050565b60008060408385031215613fe157613fe0613d35565b5b6000613fef85828601613fb5565b925050602061400085828601613d60565b9150509250929050565b61401381613e51565b811461401e57600080fd5b50565b6000813590506140308161400a565b92915050565b60006020828403121561404c5761404b613d35565b5b600061405a84828501614021565b91505092915050565b60008060006060848603121561407c5761407b613d35565b5b600061408a86828701613fb5565b935050602061409b86828701613fb5565b92505060406140ac86828701613d60565b9150509250925092565b600080604083850312156140cd576140cc613d35565b5b60006140db85828601613d60565b92505060206140ec85828601613d60565b9150509250929050565b600060408201905061410b6000830185613f74565b6141186020830184613da2565b9392505050565b60006020828403121561413557614134613d35565b5b600061414384828501613fb5565b91505092915050565b6000819050919050565b600061417161416c61416784613f42565b61414c565b613f42565b9050919050565b600061418382614156565b9050919050565b600061419582614178565b9050919050565b6141a58161418a565b82525050565b60006020820190506141c0600083018461419c565b92915050565b60006141d182614178565b9050919050565b6141e1816141c6565b82525050565b60006020820190506141fc60008301846141d8565b92915050565b6000806040838503121561421957614218613d35565b5b600061422785828601613fb5565b925050602061423885828601614021565b9150509250929050565b600080fd5b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61428482613ed6565b810181811067ffffffffffffffff821117156142a3576142a261424c565b5b80604052505050565b60006142b6613d2b565b90506142c2828261427b565b919050565b600067ffffffffffffffff8211156142e2576142e161424c565b5b6142eb82613ed6565b9050602081019050919050565b82818337600083830152505050565b600061431a614315846142c7565b6142ac565b90508281526020810184848401111561433657614335614247565b5b6143418482856142f8565b509392505050565b600082601f83011261435e5761435d614242565b5b813561436e848260208601614307565b91505092915050565b6000806000806080858703121561439157614390613d35565b5b600061439f87828801613fb5565b94505060206143b087828801613fb5565b93505060406143c187828801613d60565b925050606085013567ffffffffffffffff8111156143e2576143e1613d3a565b5b6143ee87828801614349565b91505092959194509250565b600067ffffffffffffffff8211156144155761441461424c565b5b602082029050602081019050919050565b600080fd5b600061443e614439846143fa565b6142ac565b9050808382526020820190506020840283018581111561446157614460614426565b5b835b8181101561448a57806144768882613fb5565b845260208401935050602081019050614463565b5050509392505050565b600082601f8301126144a9576144a8614242565b5b81356144b984826020860161442b565b91505092915050565b6000602082840312156144d8576144d7613d35565b5b600082013567ffffffffffffffff8111156144f6576144f5613d3a565b5b61450284828501614494565b91505092915050565b600080fd5b600061010082840312156145275761452661450b565b5b6145326101006142ac565b9050600061454284828501613d60565b600083015250602061455684828501613d60565b602083015250604061456a84828501613d60565b604083015250606061457e84828501613d60565b606083015250608061459284828501613d60565b60808301525060a06145a684828501613d60565b60a08301525060c06145ba84828501613d60565b60c08301525060e06145ce84828501613d60565b60e08301525092915050565b600061010082840312156145f1576145f0613d35565b5b60006145ff84828501614510565b91505092915050565b6000806040838503121561461f5761461e613d35565b5b600061462d85828601613fb5565b925050602061463e85828601613fb5565b9150509250929050565b60006bffffffffffffffffffffffff82169050919050565b61466981614648565b811461467457600080fd5b50565b60008135905061468681614660565b92915050565b6000602082840312156146a2576146a1613d35565b5b60006146b084828501614677565b91505092915050565b6000819050919050565b6146cc816146b9565b81146146d757600080fd5b50565b6000813590506146e9816146c3565b92915050565b60006020828403121561470557614704613d35565b5b6000614713848285016146da565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061476357607f821691505b602082108114156147775761477661471c565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006147b782613d3f565b91506147c283613d3f565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156147fb576147fa61477d565b5b828202905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061484082613d3f565b915061484b83613d3f565b92508261485b5761485a614806565b5b828204905092915050565b7f6d696e74696e67206e6565647320746f2062652061637469766520746f206d6960008201527f6e74000000000000000000000000000000000000000000000000000000000000602082015250565b60006148c2602283613e92565b91506148cd82614866565b604082019050919050565b600060208201905081810360008301526148f1816148b5565b9050919050565b7f6d696e74696e67206e6565647320746f20626520646f6e65206265666f72652060008201527f50726f6f66206f66205374616b65000000000000000000000000000000000000602082015250565b6000614954602e83613e92565b915061495f826148f8565b604082019050919050565b6000602082019050818103600083015261498381614947565b9050919050565b7f656d657267656e63792073687574646f776e20697320696e20706c6163650000600082015250565b60006149c0601e83613e92565b91506149cb8261498a565b602082019050919050565b600060208201905081810360008301526149ef816149b3565b9050919050565b6000604082019050614a0b6000830185613da2565b614a186020830184613f74565b9392505050565b6000614a2a82613d3f565b9150614a3583613d3f565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115614a6a57614a6961477d565b5b828201905092915050565b7f6578636565646564206d6178206e756d626572206f66206d696e747300000000600082015250565b6000614aab601c83613e92565b9150614ab682614a75565b602082019050919050565b60006020820190508181036000830152614ada81614a9e565b9050919050565b7f63616c6c657220697320626c61636b6c69737465640000000000000000000000600082015250565b6000614b17601583613e92565b9150614b2282614ae1565b602082019050919050565b60006020820190508181036000830152614b4681614b0a565b9050919050565b7f696e73756666696369656e742066756e647320746f2070617920666f72206d6960008201527f6e74000000000000000000000000000000000000000000000000000000000000602082015250565b6000614ba9602283613e92565b9150614bb482614b4d565b604082019050919050565b60006020820190508181036000830152614bd881614b9c565b9050919050565b600081905092915050565b50565b6000614bfa600083614bdf565b9150614c0582614bea565b600082019050919050565b6000614c1b82614bed565b9150819050919050565b6000614c3082613d3f565b9150614c3b83613d3f565b925082821015614c4e57614c4d61477d565b5b828203905092915050565b614c6281613d3f565b82525050565b60a082016000820151614c7e6000850182614c59565b506020820151614c916020850182614c59565b506040820151614ca46040850182614c59565b506060820151614cb76060850182614c59565b506080820151614cca6080850182614c59565b50505050565b600060a082019050614ce56000830184614c68565b92915050565b600081519050614cfa81613d49565b92915050565b600060208284031215614d1657614d15613d35565b5b6000614d2484828501614ceb565b91505092915050565b6000614d3882613d3f565b91506000821415614d4c57614d4b61477d565b5b600182039050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000614d9182613d3f565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415614dc457614dc361477d565b5b600182019050919050565b7f55524920717565727920666f72206e6f6e6578697374656e7420746f6b656e00600082015250565b6000614e05601f83613e92565b9150614e1082614dcf565b602082019050919050565b60006020820190508181036000830152614e3481614df8565b9050919050565b6000819050919050565b614e56614e5182613d3f565b614e3b565b82525050565b6000614e688285614e45565b602082019150614e788284614e45565b6020820191508190509392505050565b600081519050919050565b600082825260208201905092915050565b6000614eaf82614e88565b614eb98185614e93565b9350614ec9818560208601613ea3565b614ed281613ed6565b840191505092915050565b6000604082019050614ef26000830185613da2565b8181036020830152614f048184614ea4565b90509392505050565b600067ffffffffffffffff821115614f2857614f2761424c565b5b602082029050602081019050919050565b6000614f4c614f4784614f0d565b6142ac565b90508083825260208201905060208402830185811115614f6f57614f6e614426565b5b835b81811015614f985780614f848882614ceb565b845260208401935050602081019050614f71565b5050509392505050565b600082601f830112614fb757614fb6614242565b5b8151614fc7848260208601614f39565b91505092915050565b600067ffffffffffffffff821115614feb57614fea61424c565b5b602082029050602081019050919050565b600061ffff82169050919050565b61501381614ffc565b811461501e57600080fd5b50565b6000815190506150308161500a565b92915050565b600061504961504484614fd0565b6142ac565b9050808382526020820190506020840283018581111561506c5761506b614426565b5b835b8181101561509557806150818882615021565b84526020840193505060208101905061506e565b5050509392505050565b600082601f8301126150b4576150b3614242565b5b81516150c4848260208601615036565b91505092915050565b600080604083850312156150e4576150e3613d35565b5b600083015167ffffffffffffffff81111561510257615101613d3a565b5b61510e85828601614fa2565b925050602083015167ffffffffffffffff81111561512f5761512e613d3a565b5b61513b8582860161509f565b9150509250929050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b61517a81614ffc565b82525050565b600061518c8383615171565b60208301905092915050565b6000602082019050919050565b60006151b082615145565b6151ba8185615150565b93506151c583615161565b8060005b838110156151f65781516151dd8882615180565b97506151e883615198565b9250506001810190506151c9565b5085935050505092915050565b6000602082019050818103600083015261521d81846151a5565b905092915050565b600067ffffffffffffffff8211156152405761523f61424c565b5b61524982613ed6565b9050602081019050919050565b600061526961526484615225565b6142ac565b90508281526020810184848401111561528557615284614247565b5b615290848285613ea3565b509392505050565b600082601f8301126152ad576152ac614242565b5b81516152bd848260208601615256565b91505092915050565b6000602082840312156152dc576152db613d35565b5b600082015167ffffffffffffffff8111156152fa576152f9613d3a565b5b61530684828501615298565b91505092915050565b600081905092915050565b7f7b226e616d65223a202253746174756520230000000000000000000000000000600082015250565b600061535060128361530f565b915061535b8261531a565b601282019050919050565b600061537182613e87565b61537b818561530f565b935061538b818560208601613ea3565b80840191505092915050565b7f222c20226465736372697074696f6e223a202200000000000000000000000000600082015250565b60006153cd60138361530f565b91506153d882615397565b601382019050919050565b7f412050726f6f66206f66204265617574792070726f6a6563742e2046756c6c7960008201527f206f6e2d636861696e2067656e65726174697665207374617475657320746f2060208201527f72656d656d62657220746865204d455247452e00000000000000000000000000604082015250565b600061546560538361530f565b9150615470826153e3565b605382019050919050565b7f222c2022696d616765223a202200000000000000000000000000000000000000600082015250565b60006154b1600d8361530f565b91506154bc8261547b565b600d82019050919050565b7f222c20226173706563745f726174696f223a2022310000000000000000000000600082015250565b60006154fd60158361530f565b9150615508826154c7565b601582019050919050565b7f222c202261747472696275746573223a20000000000000000000000000000000600082015250565b600061554960118361530f565b915061555482615513565b601182019050919050565b7f7d00000000000000000000000000000000000000000000000000000000000000600082015250565b600061559560018361530f565b91506155a08261555f565b600182019050919050565b60006155b682615343565b91506155c28286615366565b91506155cd826153c0565b91506155d882615458565b91506155e3826154a4565b91506155ef8285615366565b91506155fa826154f0565b91506156058261553c565b91506156118284615366565b915061561c82615588565b9150819050949350505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c000000600082015250565b600061565f601d8361530f565b915061566a82615629565b601d82019050919050565b600061568082615652565b915061568c8284615366565b915081905092915050565b7f7b226e616d65223a220000000000000000000000000000000000000000000000600082015250565b60006156cd60098361530f565b91506156d882615697565b600982019050919050565b60008190508160005260206000209050919050565b600081546157058161474b565b61570f818661530f565b9450600182166000811461572a576001811461573b5761576e565b60ff1983168652818601935061576e565b615744856156e3565b60005b8381101561576657815481890152600182019150602081019050615747565b838801955050505b50505092915050565b7f222c20226465736372697074696f6e223a2022412050726f6f66206f6620426560008201527f617574792070726f6a6563742e2046756c6c79206f6e2d636861696e2067656e60208201527f65726174697665207374617475657320746f2072656d656d626572207468652060408201527f4d455247452e0000000000000000000000000000000000000000000000000000606082015250565b600061581f60668361530f565b915061582a82615777565b606682019050919050565b7f222c202265787465726e616c5f6c696e6b223a202268747470733a2f2f6d657260008201527f67652e706f622e73747564696f2f000000000000000000000000000000000000602082015250565b6000615891602e8361530f565b915061589c82615835565b602e82019050919050565b7f222c2022696d616765223a202268747470733a2f2f6d657267652e706f622e7360008201527f747564696f2f6173736574732f6c6f676f2e706e6722207d0000000000000000602082015250565b600061590360388361530f565b915061590e826158a7565b603882019050919050565b6000615924826156c0565b915061593082846156f8565b915061593b82615812565b915061594682615884565b9150615951826158f6565b915081905092915050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b60006159b8602683613e92565b91506159c38261595c565b604082019050919050565b600060208201905081810360008301526159e7816159ab565b9050919050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b6000615a24602083613e92565b9150615a2f826159ee565b602082019050919050565b60006020820190508181036000830152615a5381615a17565b9050919050565b6000608082019050615a6f6000830187613f74565b615a7c6020830186613f74565b615a896040830185613da2565b8181036060830152615a9b8184614ea4565b905095945050505050565b600081519050615ab581613df8565b92915050565b600060208284031215615ad157615ad0613d35565b5b6000615adf84828501615aa6565b91505092915050565b6000615af382613d3f565b9150615afe83613d3f565b925082615b0e57615b0d614806565b5b828206905092915050565b7f455243323938313a20726f79616c7479206665652077696c6c2065786365656460008201527f2073616c65507269636500000000000000000000000000000000000000000000602082015250565b6000615b75602a83613e92565b9150615b8082615b19565b604082019050919050565b60006020820190508181036000830152615ba481615b68565b9050919050565b7f455243323938313a20696e76616c696420726563656976657200000000000000600082015250565b6000615be1601983613e92565b9150615bec82615bab565b602082019050919050565b60006020820190508181036000830152615c1081615bd4565b905091905056fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa2646970667358221220185d396c1df4741f7ea61295dd0f14556560a8b20bd6d15a3bb2361615f4f74664736f6c63430008090033000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000028558ba4343feb2709ed7a9531b72402a7794d8d0000000000000000000000007645eec8bb51862a5aa855c40971b2877dae81af000000000000000000000000ce791e234f869d2ccc6619c928de152eafaad162000000000000000000000000f3e33c009b658d68b8bd32871d55716df358eb3d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002dc6c00000000000000000000000000000000000000000000000000000000000cb73590000000000000000000000000000000000000000000000000000000027bc86aa000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002dc6c000000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000208d9000000000000000000000000000000000000000000000000000000000000000e4550494353202f2f204d4552474500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054d45524745000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x6080604052600436106103965760003560e01c8063715018a6116101dc578063c489744b11610102578063e7eba1ee116100a0578063ee4950021161006f578063ee49500214610dbd578063f0f4426014610de8578063f2fde38b14610e11578063ff31f71b14610e3a57610396565b8063e7eba1ee14610ced578063e8a3d48514610d2a578063e985e9c514610d55578063eb91d37e14610d9257610396565b8063c87b56dd116100dc578063c87b56dd14610c35578063c8f05d2b14610c72578063cac9266914610c9b578063cd2d9af914610cc457610396565b8063c489744b14610ba4578063c65f543214610be1578063c86f1a0214610c0c57610396565b80639aa0ea191161017a578063ae5e97d311610149578063ae5e97d314610aec578063b88d4fde14610b29578063bf77249114610b52578063c3e29cfc14610b7b57610396565b80639aa0ea1914610a1e578063a030695014610a5b578063a22cb46514610a98578063a9874b2a14610ac157610396565b80638a162b9c116101b65780638a162b9c146109605780638c18f2f11461099d5780638da5cb5b146109c857806395d89b41146109f357610396565b8063715018a6146108f35780637165485d1461090a578063763c07881461093557610396565b80633602175c116102c15780635f57697c1161025f5780636352211e1161022e5780636352211e14610823578063656bb5651461086057806369668fe91461088b57806370a08231146108b657610396565b80635f57697c1461077957806361d027b3146107a45780636224bcfd146107cf57806362b9cc13146107fa57610396565b806342842e0e1161029b57806342842e0e146106d357806348dc9d2b146106fc578063576f71391461072557806358258c2b1461074e57610396565b80633602175c146106615780633a589b971461068c57806340c10f19146106b757610396565b806314c644021161033957806326987b601161030857806326987b60146105a45780632750fc78146105cf5780632a55205a146105f85780633403c2fc1461063657610396565b806314c64402146104fc57806318160ddd1461052557806322f3e2d41461055057806323b872dd1461057b57610396565b8063081812fc11610375578063081812fc14610440578063095ea7b31461047d578063119552a1146104a657806312a39614146104d157610396565b8062b7d5e11461039b57806301ffc9a7146103d857806306fdde0314610415575b600080fd5b3480156103a757600080fd5b506103c260048036038101906103bd9190613d75565b610e65565b6040516103cf9190613db1565b60405180910390f35b3480156103e457600080fd5b506103ff60048036038101906103fa9190613e24565b610e7d565b60405161040c9190613e6c565b60405180910390f35b34801561042157600080fd5b5061042a610e8f565b6040516104379190613f20565b60405180910390f35b34801561044c57600080fd5b5061046760048036038101906104629190613d75565b610f21565b6040516104749190613f83565b60405180910390f35b34801561048957600080fd5b506104a4600480360381019061049f9190613fca565b610f9d565b005b3480156104b257600080fd5b506104bb6110a8565b6040516104c89190613db1565b60405180910390f35b3480156104dd57600080fd5b506104e66110ae565b6040516104f39190613db1565b60405180910390f35b34801561050857600080fd5b50610523600480360381019061051e9190614036565b6110b4565b005b34801561053157600080fd5b5061053a611110565b6040516105479190613db1565b60405180910390f35b34801561055c57600080fd5b50610565611127565b6040516105729190613e6c565b60405180910390f35b34801561058757600080fd5b506105a2600480360381019061059d9190614063565b61113a565b005b3480156105b057600080fd5b506105b961114a565b6040516105c69190613db1565b60405180910390f35b3480156105db57600080fd5b506105f660048036038101906105f19190614036565b611153565b005b34801561060457600080fd5b5061061f600480360381019061061a91906140b6565b6111be565b60405161062d9291906140f6565b60405180910390f35b34801561064257600080fd5b5061064b6113a9565b6040516106589190613e6c565b60405180910390f35b34801561066d57600080fd5b506106766113bc565b6040516106839190613db1565b60405180910390f35b34801561069857600080fd5b506106a16113c2565b6040516106ae9190613f83565b60405180910390f35b6106d160048036038101906106cc9190613fca565b6113e8565b005b3480156106df57600080fd5b506106fa60048036038101906106f59190614063565b6117a2565b005b34801561070857600080fd5b50610723600480360381019061071e919061411f565b6117c2565b005b34801561073157600080fd5b5061074c60048036038101906107479190613d75565b61180e565b005b34801561075a57600080fd5b50610763611820565b6040516107709190613e6c565b60405180910390f35b34801561078557600080fd5b5061078e61183e565b60405161079b9190613db1565b60405180910390f35b3480156107b057600080fd5b506107b9611844565b6040516107c69190613f83565b60405180910390f35b3480156107db57600080fd5b506107e461186a565b6040516107f191906141ab565b60405180910390f35b34801561080657600080fd5b50610821600480360381019061081c919061411f565b611890565b005b34801561082f57600080fd5b5061084a60048036038101906108459190613d75565b6118dc565b6040516108579190613f83565b60405180910390f35b34801561086c57600080fd5b506108756118f2565b6040516108829190613db1565b60405180910390f35b34801561089757600080fd5b506108a06118f8565b6040516108ad9190613db1565b60405180910390f35b3480156108c257600080fd5b506108dd60048036038101906108d8919061411f565b6118fe565b6040516108ea9190613db1565b60405180910390f35b3480156108ff57600080fd5b506109086119ce565b005b34801561091657600080fd5b5061091f6119e2565b60405161092c91906141e7565b60405180910390f35b34801561094157600080fd5b5061094a611a08565b6040516109579190613db1565b60405180910390f35b34801561096c57600080fd5b506109876004803603810190610982919061411f565b611a1d565b6040516109949190613db1565b60405180910390f35b3480156109a957600080fd5b506109b2611bbe565b6040516109bf9190613db1565b60405180910390f35b3480156109d457600080fd5b506109dd611bc4565b6040516109ea9190613f83565b60405180910390f35b3480156109ff57600080fd5b50610a08611bee565b604051610a159190613f20565b60405180910390f35b348015610a2a57600080fd5b50610a456004803603810190610a40919061411f565b611c80565b604051610a529190613e6c565b60405180910390f35b348015610a6757600080fd5b50610a826004803603810190610a7d919061411f565b611ca0565b604051610a8f9190613db1565b60405180910390f35b348015610aa457600080fd5b50610abf6004803603810190610aba9190614202565b611cfe565b005b348015610acd57600080fd5b50610ad6611e76565b604051610ae39190613db1565b60405180910390f35b348015610af857600080fd5b50610b136004803603810190610b0e9190613d75565b611e7c565b604051610b209190613db1565b60405180910390f35b348015610b3557600080fd5b50610b506004803603810190610b4b9190614377565b611f29565b005b348015610b5e57600080fd5b50610b796004803603810190610b7491906144c2565b611fa5565b005b348015610b8757600080fd5b50610ba26004803603810190610b9d91906145da565b612040565b005b348015610bb057600080fd5b50610bcb6004803603810190610bc69190614608565b61208d565b604051610bd89190613db1565b60405180910390f35b348015610bed57600080fd5b50610bf6612120565b604051610c039190613db1565b60405180910390f35b348015610c1857600080fd5b50610c336004803603810190610c2e9190613d75565b612126565b005b348015610c4157600080fd5b50610c5c6004803603810190610c579190613d75565b612138565b604051610c699190613f20565b60405180910390f35b348015610c7e57600080fd5b50610c996004803603810190610c949190613d75565b61242d565b005b348015610ca757600080fd5b50610cc26004803603810190610cbd919061468c565b61243f565b005b348015610cd057600080fd5b50610ceb6004803603810190610ce6919061411f565b612476565b005b348015610cf957600080fd5b50610d146004803603810190610d0f91906146ef565b6124c2565b604051610d219190613db1565b60405180910390f35b348015610d3657600080fd5b50610d3f6124da565b604051610d4c9190613f20565b60405180910390f35b348015610d6157600080fd5b50610d7c6004803603810190610d779190614608565b612529565b604051610d899190613e6c565b60405180910390f35b348015610d9e57600080fd5b50610da76125bd565b604051610db49190613db1565b60405180910390f35b348015610dc957600080fd5b50610dd26126bf565b604051610ddf9190613db1565b60405180910390f35b348015610df457600080fd5b50610e0f6004803603810190610e0a919061411f565b6126c5565b005b348015610e1d57600080fd5b50610e386004803603810190610e33919061411f565b612711565b005b348015610e4657600080fd5b50610e4f612795565b604051610e5c9190613db1565b60405180910390f35b601a6020528060005260406000206000915090505481565b6000610e888261279b565b9050919050565b606060028054610e9e9061474b565b80601f0160208091040260200160405190810160405280929190818152602001828054610eca9061474b565b8015610f175780601f10610eec57610100808354040283529160200191610f17565b820191906000526020600020905b815481529060010190602001808311610efa57829003601f168201915b5050505050905090565b6000610f2c82612815565b610f62576040517fcf4700e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6006600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b6000610fa8826118dc565b90508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415611010576040517f943f7b8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1661102f612863565b73ffffffffffffffffffffffffffffffffffffffff1614158015611061575061105f8161105a612863565b612529565b155b15611098576040517fcfb3b94200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110a383838361286b565b505050565b60125481565b600b5481565b6110bc61291d565b80601b60006101000a81548160ff0219169083151502179055507fb28c3d380b31a9985118fb02f15e73682e9338a972a19723b46ac25b4feb9dc0816040516111059190613e6c565b60405180910390a150565b600061111a61299b565b6001546000540303905090565b600d60009054906101000a900460ff1681565b6111458383836129a0565b505050565b60008054905090565b61115b61291d565b80600d60006101000a81548160ff0219169083151502179055507fa087c8b791d7a9eaf08393fedb4bc4f0e697ebc3f2538f7184b505925a32489f600d60009054906101000a900460ff166040516111b39190613e6c565b60405180910390a150565b6000806000600960008681526020019081526020016000206040518060400160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016000820160149054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff166bffffffffffffffffffffffff16815250509050600073ffffffffffffffffffffffffffffffffffffffff16816000015173ffffffffffffffffffffffffffffffffffffffff1614156113545760086040518060400160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016000820160149054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff166bffffffffffffffffffffffff168152505090505b600061135e612e56565b6bffffffffffffffffffffffff1682602001516bffffffffffffffffffffffff168661138a91906147ac565b6113949190614835565b90508160000151819350935050509250929050565b601b60009054906101000a900460ff1681565b600f5481565b601760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600d60009054906101000a900460ff16611437576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161142e906148d8565b60405180910390fd5b61143f611820565b1561147f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114769061496a565b60405180910390fd5b601b60009054906101000a900460ff16156114cf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114c6906149d6565b60405180910390fd5b600043336040516020016114e49291906149f6565b604051602081830303815290604052805190602001209050600b5482601c60008481526020019081526020016000205461151e9190614a1f565b111561155f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161155690614ac1565b60405180910390fd5b601d60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156115ec576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016115e390614b2d565b60405180910390fd5b6000826115f76125bd565b61160191906147ac565b905034811115611646576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161163d90614bbf565b60405180910390fd5b600061165133611a1d565b905080601a6000805481526020019081526020016000208190555083601c60008581526020019081526020016000205461168b9190614a1f565b601c6000858152602001908152602001600020819055506116be8585604051806020016040528060008152506000612e60565b601660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168260405161170490614c10565b60006040518083038185875af1925050503d8060008114611741576040519150601f19603f3d011682016040523d82523d6000602084013e611746565b606091505b5050503373ffffffffffffffffffffffffffffffffffffffff166108fc833461176f9190614c25565b9081150290604051600060405180830381858888f1935050505015801561179a573d6000803e3d6000fd5b505050505050565b6117bd83838360405180602001604052806000815250611f29565b505050565b6117ca61291d565b80600d60016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b61181661291d565b80600c8190555050565b6000680100000000000000004411806118395750600044145b905090565b60145481565b601660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b601860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b61189861291d565b80601860006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60006118e78261322e565b600001519050919050565b60105481565b600c5481565b60008073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611966576040517f8f4eb60400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600560008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900467ffffffffffffffff1667ffffffffffffffff169050919050565b6119d661291d565b6119e060006134bd565b565b600d60019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600c5442611a189190614c25565b905090565b6000611a27613cb9565b611a2f611a08565b8160000181815250506012548160200181815250506013548160400181815250506014548160600181815250506015548160800181815250506000600d60019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c6159199836040518263ffffffff1660e01b8152600401611ac59190614cd0565b60206040518083038186803b158015611add57600080fd5b505afa158015611af1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b159190614d00565b90503073ffffffffffffffffffffffffffffffffffffffff1663a0306950856040518263ffffffff1660e01b8152600401611b509190613f83565b60206040518083038186803b158015611b6857600080fd5b505afa925050508015611b9957506040513d601f19601f82011682018060405250810190611b969190614d00565b60015b611ba7578092505050611bb9565b8082611bb39190614a1f565b93505050505b919050565b60155481565b6000600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b606060038054611bfd9061474b565b80601f0160208091040260200160405190810160405280929190818152602001828054611c299061474b565b8015611c765780601f10611c4b57610100808354040283529160200191611c76565b820191906000526020600020905b815481529060010190602001808311611c5957829003601f168201915b5050505050905090565b601d6020528060005260406000206000915054906101000a900460ff1681565b600080611ccf601760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168461208d565b905060006010821015611ce25781611ce5565b60105b905060195481611cf591906147ac565b92505050919050565b611d06612863565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611d6b576040517fb06307db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060076000611d78612863565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff16611e25612863565b73ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3183604051611e6a9190613e6c565b60405180910390a35050565b60115481565b60008082905080611e8b61299b565b11158015611e9a575060005481105b15611ef2575b600115611ef1576000601a60008381526020019081526020016000205414611ede57601a600082815260200190815260200160002054915050611f24565b8080611ee990614d2d565b915050611ea0565b5b6040517fdf2d9b4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b919050565b611f348484846129a0565b611f538373ffffffffffffffffffffffffffffffffffffffff16613583565b8015611f685750611f66848484846135a6565b155b15611f9f576040517fd1a57ed600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b611fad61291d565b60005b815181101561203c576001601d6000848481518110611fd257611fd1614d57565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508061203590614d86565b9050611fb0565b5050565b61204861291d565b8060000151600e819055508060200151600f81905550806040015160108190555080608001516012819055508060a001516013819055508060c0015160148190555050565b60008273ffffffffffffffffffffffffffffffffffffffff166370a08231836040518263ffffffff1660e01b81526004016120c89190613f83565b60206040518083038186803b1580156120e057600080fd5b505afa1580156120f4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121189190614d00565b905092915050565b600e5481565b61212e61291d565b80600b8190555050565b606061214382612815565b612182576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161217990614e1b565b60405180910390fd5b600061218d83611e7c565b9050600081846040516020016121a4929190614e5c565b60405160208183030381529060405290506000601860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c5141cfe84846040518363ffffffff1660e01b8152600401612214929190614edd565b60006040518083038186803b15801561222c57600080fd5b505afa158015612240573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525081019061226991906150cd565b9150506000601860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663be496f0e836040518263ffffffff1660e01b81526004016122c99190615203565b60006040518083038186803b1580156122e157600080fd5b505afa1580156122f5573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525081019061231e91906152c6565b905061240361232c87613706565b82601860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166352ed580c866040518263ffffffff1660e01b81526004016123889190615203565b60006040518083038186803b1580156123a057600080fd5b505afa1580156123b4573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906123dd91906152c6565b6040516020016123ef939291906155ab565b604051602081830303815290604052613867565b6040516020016124139190615675565b604051602081830303815290604052945050505050919050565b61243561291d565b8060198190555050565b61244761291d565b612473601660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16826139cb565b50565b61247e61291d565b80601760006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b601c6020528060005260406000206000915090505481565b606061250560026040516020016124f19190615919565b604051602081830303815290604052613867565b6040516020016125159190615675565b604051602081830303815290604052905090565b6000600760008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b60006125c7613cb9565b6125cf611a08565b816000018181525050600e54816020018181525050600f548160400181815250506010548160600181815250506011548160800181815250506000600d60019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c6159199836040518263ffffffff1660e01b81526004016126659190614cd0565b60206040518083038186803b15801561267d57600080fd5b505afa158015612691573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126b59190614d00565b9050809250505090565b60135481565b6126cd61291d565b80601660006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b61271961291d565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415612789576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612780906159ce565b60405180910390fd5b612792816134bd565b50565b60195481565b60007f2a55205a000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061280e575061280d82613b61565b5b9050919050565b60008161282061299b565b1115801561282f575060005482105b801561285c575060046000838152602001908152602001600020600001601c9054906101000a900460ff16155b9050919050565b600033905090565b826006600084815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550818373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a4505050565b612925612863565b73ffffffffffffffffffffffffffffffffffffffff16612943611bc4565b73ffffffffffffffffffffffffffffffffffffffff1614612999576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161299090615a3a565b60405180910390fd5b565b600090565b60006129ab8261322e565b90508373ffffffffffffffffffffffffffffffffffffffff16816000015173ffffffffffffffffffffffffffffffffffffffff1614612a16576040517fa114810000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008473ffffffffffffffffffffffffffffffffffffffff16612a37612863565b73ffffffffffffffffffffffffffffffffffffffff161480612a665750612a6585612a60612863565b612529565b5b80612aab5750612a74612863565b73ffffffffffffffffffffffffffffffffffffffff16612a9384610f21565b73ffffffffffffffffffffffffffffffffffffffff16145b905080612ae4576040517f59c896be00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161415612b4b576040517fea553b3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612b588585856001613c43565b612b646000848761286b565b6001600560008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160008282829054906101000a900467ffffffffffffffff160392506101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506001600560008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160008282829054906101000a900467ffffffffffffffff160192506101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506000600460008581526020019081526020016000209050848160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550428160000160146101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555060006001850190506000600460008381526020019081526020016000209050600073ffffffffffffffffffffffffffffffffffffffff168160000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415612de4576000548214612de357878160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555084602001518160000160146101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055505b5b505050828473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4612e4f8585856001613c49565b5050505050565b6000612710905090565b600080549050600073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161415612ecd576040517f2e07630000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000841415612f08576040517fb562e8dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612f156000868387613c43565b83600560008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160008282829054906101000a900467ffffffffffffffff160192506101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555083600560008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160088282829054906101000a900467ffffffffffffffff160192506101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550846004600083815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550426004600083815260200190815260200160002060000160146101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506000819050600085820190508380156130df57506130de8773ffffffffffffffffffffffffffffffffffffffff16613583565b5b156131a5575b818773ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a461315460008884806001019550886135a6565b61318a576040517fd1a57ed600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b808214156130e55782600054146131a057600080fd5b613211565b5b818060010192508773ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4808214156131a6575b8160008190555050506132276000868387613c49565b5050505050565b613236613ce8565b60008290508061324461299b565b11158015613253575060005481105b15613486576000600460008381526020019081526020016000206040518060600160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016000820160149054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff16815260200160008201601c9054906101000a900460ff1615151515815250509050806040015161348457600073ffffffffffffffffffffffffffffffffffffffff16816000015173ffffffffffffffffffffffffffffffffffffffff16146133685780925050506134b8565b5b60011561348357818060019003925050600460008381526020019081526020016000206040518060600160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016000820160149054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff16815260200160008201601c9054906101000a900460ff1615151515815250509050600073ffffffffffffffffffffffffffffffffffffffff16816000015173ffffffffffffffffffffffffffffffffffffffff161461347e5780925050506134b8565b613369565b5b505b6040517fdf2d9b4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b919050565b6000600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600a60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b60008373ffffffffffffffffffffffffffffffffffffffff1663150b7a026135cc612863565b8786866040518563ffffffff1660e01b81526004016135ee9493929190615a5a565b602060405180830381600087803b15801561360857600080fd5b505af192505050801561363957506040513d601f19601f820116820180604052508101906136369190615abb565b60015b6136b3573d8060008114613669576040519150601f19603f3d011682016040523d82523d6000602084013e61366e565b606091505b506000815114156136ab576040517fd1a57ed600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805181602001fd5b63150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614915050949350505050565b6060600082141561374e576040518060400160405280600181526020017f30000000000000000000000000000000000000000000000000000000000000008152509050613862565b600082905060005b6000821461378057808061376990614d86565b915050600a826137799190614835565b9150613756565b60008167ffffffffffffffff81111561379c5761379b61424c565b5b6040519080825280601f01601f1916602001820160405280156137ce5781602001600182028036833780820191505090505b5090505b6000851461385b576001826137e79190614c25565b9150600a856137f69190615ae8565b60306138029190614a1f565b60f81b81838151811061381857613817614d57565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600a856138549190614835565b94506137d2565b8093505050505b919050565b606060008251141561388a576040518060200160405280600081525090506139c6565b6000604051806060016040528060408152602001615c1860409139905060006003600285516138b99190614a1f565b6138c39190614835565b60046138cf91906147ac565b67ffffffffffffffff8111156138e8576138e761424c565b5b6040519080825280601f01601f19166020018201604052801561391a5781602001600182028036833780820191505090505b509050600182016020820185865187015b80821015613986576003820191508151603f8160121c168501518453600184019350603f81600c1c168501518453600184019350603f8160061c168501518453600184019350603f811685015184536001840193505061392b565b50506003865106600181146139a257600281146139b5576139bd565b603d6001830353603d60028303536139bd565b603d60018303535b50505080925050505b919050565b6139d3612e56565b6bffffffffffffffffffffffff16816bffffffffffffffffffffffff161115613a31576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613a2890615b8b565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415613aa1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613a9890615bf7565b60405180910390fd5b60405180604001604052808373ffffffffffffffffffffffffffffffffffffffff168152602001826bffffffffffffffffffffffff16815250600860008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160000160146101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055509050505050565b60007f80ac58cd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480613c2c57507f5b5e139f000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b80613c3c5750613c3b82613c4f565b5b9050919050565b50505050565b50505050565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b6040518060600160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001600067ffffffffffffffff1681526020016000151581525090565b6000604051905090565b600080fd5b600080fd5b6000819050919050565b613d5281613d3f565b8114613d5d57600080fd5b50565b600081359050613d6f81613d49565b92915050565b600060208284031215613d8b57613d8a613d35565b5b6000613d9984828501613d60565b91505092915050565b613dab81613d3f565b82525050565b6000602082019050613dc66000830184613da2565b92915050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b613e0181613dcc565b8114613e0c57600080fd5b50565b600081359050613e1e81613df8565b92915050565b600060208284031215613e3a57613e39613d35565b5b6000613e4884828501613e0f565b91505092915050565b60008115159050919050565b613e6681613e51565b82525050565b6000602082019050613e816000830184613e5d565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b83811015613ec1578082015181840152602081019050613ea6565b83811115613ed0576000848401525b50505050565b6000601f19601f8301169050919050565b6000613ef282613e87565b613efc8185613e92565b9350613f0c818560208601613ea3565b613f1581613ed6565b840191505092915050565b60006020820190508181036000830152613f3a8184613ee7565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000613f6d82613f42565b9050919050565b613f7d81613f62565b82525050565b6000602082019050613f986000830184613f74565b92915050565b613fa781613f62565b8114613fb257600080fd5b50565b600081359050613fc481613f9e565b92915050565b60008060408385031215613fe157613fe0613d35565b5b6000613fef85828601613fb5565b925050602061400085828601613d60565b9150509250929050565b61401381613e51565b811461401e57600080fd5b50565b6000813590506140308161400a565b92915050565b60006020828403121561404c5761404b613d35565b5b600061405a84828501614021565b91505092915050565b60008060006060848603121561407c5761407b613d35565b5b600061408a86828701613fb5565b935050602061409b86828701613fb5565b92505060406140ac86828701613d60565b9150509250925092565b600080604083850312156140cd576140cc613d35565b5b60006140db85828601613d60565b92505060206140ec85828601613d60565b9150509250929050565b600060408201905061410b6000830185613f74565b6141186020830184613da2565b9392505050565b60006020828403121561413557614134613d35565b5b600061414384828501613fb5565b91505092915050565b6000819050919050565b600061417161416c61416784613f42565b61414c565b613f42565b9050919050565b600061418382614156565b9050919050565b600061419582614178565b9050919050565b6141a58161418a565b82525050565b60006020820190506141c0600083018461419c565b92915050565b60006141d182614178565b9050919050565b6141e1816141c6565b82525050565b60006020820190506141fc60008301846141d8565b92915050565b6000806040838503121561421957614218613d35565b5b600061422785828601613fb5565b925050602061423885828601614021565b9150509250929050565b600080fd5b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61428482613ed6565b810181811067ffffffffffffffff821117156142a3576142a261424c565b5b80604052505050565b60006142b6613d2b565b90506142c2828261427b565b919050565b600067ffffffffffffffff8211156142e2576142e161424c565b5b6142eb82613ed6565b9050602081019050919050565b82818337600083830152505050565b600061431a614315846142c7565b6142ac565b90508281526020810184848401111561433657614335614247565b5b6143418482856142f8565b509392505050565b600082601f83011261435e5761435d614242565b5b813561436e848260208601614307565b91505092915050565b6000806000806080858703121561439157614390613d35565b5b600061439f87828801613fb5565b94505060206143b087828801613fb5565b93505060406143c187828801613d60565b925050606085013567ffffffffffffffff8111156143e2576143e1613d3a565b5b6143ee87828801614349565b91505092959194509250565b600067ffffffffffffffff8211156144155761441461424c565b5b602082029050602081019050919050565b600080fd5b600061443e614439846143fa565b6142ac565b9050808382526020820190506020840283018581111561446157614460614426565b5b835b8181101561448a57806144768882613fb5565b845260208401935050602081019050614463565b5050509392505050565b600082601f8301126144a9576144a8614242565b5b81356144b984826020860161442b565b91505092915050565b6000602082840312156144d8576144d7613d35565b5b600082013567ffffffffffffffff8111156144f6576144f5613d3a565b5b61450284828501614494565b91505092915050565b600080fd5b600061010082840312156145275761452661450b565b5b6145326101006142ac565b9050600061454284828501613d60565b600083015250602061455684828501613d60565b602083015250604061456a84828501613d60565b604083015250606061457e84828501613d60565b606083015250608061459284828501613d60565b60808301525060a06145a684828501613d60565b60a08301525060c06145ba84828501613d60565b60c08301525060e06145ce84828501613d60565b60e08301525092915050565b600061010082840312156145f1576145f0613d35565b5b60006145ff84828501614510565b91505092915050565b6000806040838503121561461f5761461e613d35565b5b600061462d85828601613fb5565b925050602061463e85828601613fb5565b9150509250929050565b60006bffffffffffffffffffffffff82169050919050565b61466981614648565b811461467457600080fd5b50565b60008135905061468681614660565b92915050565b6000602082840312156146a2576146a1613d35565b5b60006146b084828501614677565b91505092915050565b6000819050919050565b6146cc816146b9565b81146146d757600080fd5b50565b6000813590506146e9816146c3565b92915050565b60006020828403121561470557614704613d35565b5b6000614713848285016146da565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061476357607f821691505b602082108114156147775761477661471c565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006147b782613d3f565b91506147c283613d3f565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156147fb576147fa61477d565b5b828202905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061484082613d3f565b915061484b83613d3f565b92508261485b5761485a614806565b5b828204905092915050565b7f6d696e74696e67206e6565647320746f2062652061637469766520746f206d6960008201527f6e74000000000000000000000000000000000000000000000000000000000000602082015250565b60006148c2602283613e92565b91506148cd82614866565b604082019050919050565b600060208201905081810360008301526148f1816148b5565b9050919050565b7f6d696e74696e67206e6565647320746f20626520646f6e65206265666f72652060008201527f50726f6f66206f66205374616b65000000000000000000000000000000000000602082015250565b6000614954602e83613e92565b915061495f826148f8565b604082019050919050565b6000602082019050818103600083015261498381614947565b9050919050565b7f656d657267656e63792073687574646f776e20697320696e20706c6163650000600082015250565b60006149c0601e83613e92565b91506149cb8261498a565b602082019050919050565b600060208201905081810360008301526149ef816149b3565b9050919050565b6000604082019050614a0b6000830185613da2565b614a186020830184613f74565b9392505050565b6000614a2a82613d3f565b9150614a3583613d3f565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115614a6a57614a6961477d565b5b828201905092915050565b7f6578636565646564206d6178206e756d626572206f66206d696e747300000000600082015250565b6000614aab601c83613e92565b9150614ab682614a75565b602082019050919050565b60006020820190508181036000830152614ada81614a9e565b9050919050565b7f63616c6c657220697320626c61636b6c69737465640000000000000000000000600082015250565b6000614b17601583613e92565b9150614b2282614ae1565b602082019050919050565b60006020820190508181036000830152614b4681614b0a565b9050919050565b7f696e73756666696369656e742066756e647320746f2070617920666f72206d6960008201527f6e74000000000000000000000000000000000000000000000000000000000000602082015250565b6000614ba9602283613e92565b9150614bb482614b4d565b604082019050919050565b60006020820190508181036000830152614bd881614b9c565b9050919050565b600081905092915050565b50565b6000614bfa600083614bdf565b9150614c0582614bea565b600082019050919050565b6000614c1b82614bed565b9150819050919050565b6000614c3082613d3f565b9150614c3b83613d3f565b925082821015614c4e57614c4d61477d565b5b828203905092915050565b614c6281613d3f565b82525050565b60a082016000820151614c7e6000850182614c59565b506020820151614c916020850182614c59565b506040820151614ca46040850182614c59565b506060820151614cb76060850182614c59565b506080820151614cca6080850182614c59565b50505050565b600060a082019050614ce56000830184614c68565b92915050565b600081519050614cfa81613d49565b92915050565b600060208284031215614d1657614d15613d35565b5b6000614d2484828501614ceb565b91505092915050565b6000614d3882613d3f565b91506000821415614d4c57614d4b61477d565b5b600182039050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000614d9182613d3f565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415614dc457614dc361477d565b5b600182019050919050565b7f55524920717565727920666f72206e6f6e6578697374656e7420746f6b656e00600082015250565b6000614e05601f83613e92565b9150614e1082614dcf565b602082019050919050565b60006020820190508181036000830152614e3481614df8565b9050919050565b6000819050919050565b614e56614e5182613d3f565b614e3b565b82525050565b6000614e688285614e45565b602082019150614e788284614e45565b6020820191508190509392505050565b600081519050919050565b600082825260208201905092915050565b6000614eaf82614e88565b614eb98185614e93565b9350614ec9818560208601613ea3565b614ed281613ed6565b840191505092915050565b6000604082019050614ef26000830185613da2565b8181036020830152614f048184614ea4565b90509392505050565b600067ffffffffffffffff821115614f2857614f2761424c565b5b602082029050602081019050919050565b6000614f4c614f4784614f0d565b6142ac565b90508083825260208201905060208402830185811115614f6f57614f6e614426565b5b835b81811015614f985780614f848882614ceb565b845260208401935050602081019050614f71565b5050509392505050565b600082601f830112614fb757614fb6614242565b5b8151614fc7848260208601614f39565b91505092915050565b600067ffffffffffffffff821115614feb57614fea61424c565b5b602082029050602081019050919050565b600061ffff82169050919050565b61501381614ffc565b811461501e57600080fd5b50565b6000815190506150308161500a565b92915050565b600061504961504484614fd0565b6142ac565b9050808382526020820190506020840283018581111561506c5761506b614426565b5b835b8181101561509557806150818882615021565b84526020840193505060208101905061506e565b5050509392505050565b600082601f8301126150b4576150b3614242565b5b81516150c4848260208601615036565b91505092915050565b600080604083850312156150e4576150e3613d35565b5b600083015167ffffffffffffffff81111561510257615101613d3a565b5b61510e85828601614fa2565b925050602083015167ffffffffffffffff81111561512f5761512e613d3a565b5b61513b8582860161509f565b9150509250929050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b61517a81614ffc565b82525050565b600061518c8383615171565b60208301905092915050565b6000602082019050919050565b60006151b082615145565b6151ba8185615150565b93506151c583615161565b8060005b838110156151f65781516151dd8882615180565b97506151e883615198565b9250506001810190506151c9565b5085935050505092915050565b6000602082019050818103600083015261521d81846151a5565b905092915050565b600067ffffffffffffffff8211156152405761523f61424c565b5b61524982613ed6565b9050602081019050919050565b600061526961526484615225565b6142ac565b90508281526020810184848401111561528557615284614247565b5b615290848285613ea3565b509392505050565b600082601f8301126152ad576152ac614242565b5b81516152bd848260208601615256565b91505092915050565b6000602082840312156152dc576152db613d35565b5b600082015167ffffffffffffffff8111156152fa576152f9613d3a565b5b61530684828501615298565b91505092915050565b600081905092915050565b7f7b226e616d65223a202253746174756520230000000000000000000000000000600082015250565b600061535060128361530f565b915061535b8261531a565b601282019050919050565b600061537182613e87565b61537b818561530f565b935061538b818560208601613ea3565b80840191505092915050565b7f222c20226465736372697074696f6e223a202200000000000000000000000000600082015250565b60006153cd60138361530f565b91506153d882615397565b601382019050919050565b7f412050726f6f66206f66204265617574792070726f6a6563742e2046756c6c7960008201527f206f6e2d636861696e2067656e65726174697665207374617475657320746f2060208201527f72656d656d62657220746865204d455247452e00000000000000000000000000604082015250565b600061546560538361530f565b9150615470826153e3565b605382019050919050565b7f222c2022696d616765223a202200000000000000000000000000000000000000600082015250565b60006154b1600d8361530f565b91506154bc8261547b565b600d82019050919050565b7f222c20226173706563745f726174696f223a2022310000000000000000000000600082015250565b60006154fd60158361530f565b9150615508826154c7565b601582019050919050565b7f222c202261747472696275746573223a20000000000000000000000000000000600082015250565b600061554960118361530f565b915061555482615513565b601182019050919050565b7f7d00000000000000000000000000000000000000000000000000000000000000600082015250565b600061559560018361530f565b91506155a08261555f565b600182019050919050565b60006155b682615343565b91506155c28286615366565b91506155cd826153c0565b91506155d882615458565b91506155e3826154a4565b91506155ef8285615366565b91506155fa826154f0565b91506156058261553c565b91506156118284615366565b915061561c82615588565b9150819050949350505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c000000600082015250565b600061565f601d8361530f565b915061566a82615629565b601d82019050919050565b600061568082615652565b915061568c8284615366565b915081905092915050565b7f7b226e616d65223a220000000000000000000000000000000000000000000000600082015250565b60006156cd60098361530f565b91506156d882615697565b600982019050919050565b60008190508160005260206000209050919050565b600081546157058161474b565b61570f818661530f565b9450600182166000811461572a576001811461573b5761576e565b60ff1983168652818601935061576e565b615744856156e3565b60005b8381101561576657815481890152600182019150602081019050615747565b838801955050505b50505092915050565b7f222c20226465736372697074696f6e223a2022412050726f6f66206f6620426560008201527f617574792070726f6a6563742e2046756c6c79206f6e2d636861696e2067656e60208201527f65726174697665207374617475657320746f2072656d656d626572207468652060408201527f4d455247452e0000000000000000000000000000000000000000000000000000606082015250565b600061581f60668361530f565b915061582a82615777565b606682019050919050565b7f222c202265787465726e616c5f6c696e6b223a202268747470733a2f2f6d657260008201527f67652e706f622e73747564696f2f000000000000000000000000000000000000602082015250565b6000615891602e8361530f565b915061589c82615835565b602e82019050919050565b7f222c2022696d616765223a202268747470733a2f2f6d657267652e706f622e7360008201527f747564696f2f6173736574732f6c6f676f2e706e6722207d0000000000000000602082015250565b600061590360388361530f565b915061590e826158a7565b603882019050919050565b6000615924826156c0565b915061593082846156f8565b915061593b82615812565b915061594682615884565b9150615951826158f6565b915081905092915050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b60006159b8602683613e92565b91506159c38261595c565b604082019050919050565b600060208201905081810360008301526159e7816159ab565b9050919050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b6000615a24602083613e92565b9150615a2f826159ee565b602082019050919050565b60006020820190508181036000830152615a5381615a17565b9050919050565b6000608082019050615a6f6000830187613f74565b615a7c6020830186613f74565b615a896040830185613da2565b8181036060830152615a9b8184614ea4565b905095945050505050565b600081519050615ab581613df8565b92915050565b600060208284031215615ad157615ad0613d35565b5b6000615adf84828501615aa6565b91505092915050565b6000615af382613d3f565b9150615afe83613d3f565b925082615b0e57615b0d614806565b5b828206905092915050565b7f455243323938313a20726f79616c7479206665652077696c6c2065786365656460008201527f2073616c65507269636500000000000000000000000000000000000000000000602082015250565b6000615b75602a83613e92565b9150615b8082615b19565b604082019050919050565b60006020820190508181036000830152615ba481615b68565b9050919050565b7f455243323938313a20696e76616c696420726563656976657200000000000000600082015250565b6000615be1601983613e92565b9150615bec82615bab565b602082019050919050565b60006020820190508181036000830152615c1081615bd4565b905091905056fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa2646970667358221220185d396c1df4741f7ea61295dd0f14556560a8b20bd6d15a3bb2361615f4f74664736f6c63430008090033

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

000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000028558ba4343feb2709ed7a9531b72402a7794d8d0000000000000000000000007645eec8bb51862a5aa855c40971b2877dae81af000000000000000000000000ce791e234f869d2ccc6619c928de152eafaad162000000000000000000000000f3e33c009b658d68b8bd32871d55716df358eb3d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002dc6c00000000000000000000000000000000000000000000000000000000000cb73590000000000000000000000000000000000000000000000000000000027bc86aa000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002dc6c000000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000208d9000000000000000000000000000000000000000000000000000000000000000e4550494353202f2f204d4552474500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054d45524745000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : config (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]

-----Encoded View---------------
19 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000020
Arg [1] : 00000000000000000000000000000000000000000000000000000000000001c0
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000200
Arg [3] : 00000000000000000000000028558ba4343feb2709ed7a9531b72402a7794d8d
Arg [4] : 0000000000000000000000007645eec8bb51862a5aa855c40971b2877dae81af
Arg [5] : 000000000000000000000000ce791e234f869d2ccc6619c928de152eafaad162
Arg [6] : 000000000000000000000000f3e33c009b658d68b8bd32871d55716df358eb3d
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [8] : 00000000000000000000000000000000000000000000000000000000002dc6c0
Arg [9] : 0000000000000000000000000000000000000000000000000000000000cb7359
Arg [10] : 0000000000000000000000000000000000000000000000000000000027bc86aa
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [12] : 00000000000000000000000000000000000000000000000000000000002dc6c0
Arg [13] : 00000000000000000000000000000000000000000000000000000000000003e8
Arg [14] : 00000000000000000000000000000000000000000000000000000000000208d9
Arg [15] : 000000000000000000000000000000000000000000000000000000000000000e
Arg [16] : 4550494353202f2f204d45524745000000000000000000000000000000000000
Arg [17] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [18] : 4d45524745000000000000000000000000000000000000000000000000000000


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

Txn Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]
[ 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.