OP Sepolia Testnet

Contract

0x099C314F792e1F91f53765Fc64AaDCcf4dCf1538
Source Code Source Code

Overview

ETH Balance

0 ETH

More Info

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Amount

There are no matching entries

Please try again later

Parent Transaction Hash Block From To Amount
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ItemCommonConsumable

Compiler Version
v0.8.23+commit.f704f362

Optimization Enabled:
Yes with 150 runs

Other Settings:
istanbul EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

// SPDX-License-Identifier: BUSL-1.1
/**
            ▒▓▒  ▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓███▓▓▒     ▒▒▒▒▓▓▓▒▓▓▓▓▓▓▓██▓
             ▒██▒▓▓▓▓█▓██████████████████▓  ▒▒▒▓███████████████▒
              ▒██▒▓█████████████████████▒ ▒▓██████████▓███████
               ▒███████████▓▒                   ▒███▓▓██████▓
                 █████████▒                     ▒▓▒▓███████▒
                  ███████▓      ▒▒▒▒▒▓▓█▓▒     ▓█▓████████
                   ▒▒▒▒▒   ▒▒▒▒▓▓▓█████▒      ▓█████████▓
                         ▒▓▓▓▒▓██████▓      ▒▓▓████████▒
                       ▒██▓▓▓███████▒      ▒▒▓███▓████
                        ▒███▓█████▒       ▒▒█████▓██▓
                          ██████▓   ▒▒▒▓██▓██▓█████▒
                           ▒▒▓▓▒   ▒██▓▒▓▓████████
                                  ▓█████▓███████▓
                                 ██▓▓██████████▒
                                ▒█████████████
                                 ███████████▓
      ▒▓▓▓▓▓▓▒▓                  ▒█████████▒                      ▒▓▓
    ▒▓█▒   ▒▒█▒▒                   ▓██████                       ▒▒▓▓▒
   ▒▒█▒       ▓▒                    ▒████                       ▒▓█▓█▓▒
   ▓▒██▓▒                             ██                       ▒▓█▓▓▓██▒
    ▓█▓▓▓▓▓█▓▓▓▒        ▒▒▒         ▒▒▒▓▓▓▓▒▓▒▒▓▒▓▓▓▓▓▓▓▓▒    ▒▓█▒ ▒▓▒▓█▓
     ▒▓█▓▓▓▓▓▓▓▓▓▓▒    ▒▒▒▓▒     ▒▒▒▓▓     ▓▓  ▓▓█▓   ▒▒▓▓   ▒▒█▒   ▒▓▒▓█▓
            ▒▒▓▓▓▒▓▒  ▒▓▓▓▒█▒   ▒▒▒█▒          ▒▒█▓▒▒▒▓▓▓▒   ▓██▓▓▓▓▓▓▓███▓
 ▒            ▒▓▓█▓  ▒▓▓▓▓█▓█▓  ▒█▓▓▒          ▓▓█▓▒▓█▓▒▒   ▓█▓        ▓███▓
▓▓▒         ▒▒▓▓█▓▒▒▓█▒   ▒▓██▓  ▓██▓▒     ▒█▓ ▓▓██   ▒▓▓▓▒▒▓█▓        ▒▓████▒
 ██▓▓▒▒▒▒▓▓███▓▒ ▒▓▓▓▓▒▒ ▒▓▓▓▓▓▓▓▒▒▒▓█▓▓▓▓█▓▓▒▒▓▓▓▓▓▒    ▒▓████▓▒     ▓▓███████▓▓▒
*/
pragma solidity 0.8.23;

import "../base/item/ConsumableItemBase.sol";

contract ItemCommonConsumable is ConsumableItemBase {

  // ---- CONSTANTS ----

  /// @notice Version of the contract
  /// @dev Should be incremented when contract changed
  string public constant override VERSION = "1.0.0";

  function itemMetaType() external pure override returns (ItemMetaType) {
    return ItemMetaType.CONSUMABLE_ITEM;
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;

import "./ItemBase.sol";
import "../../interfaces/IConsumableItem.sol";

abstract contract ConsumableItemBase is ItemBase, IConsumableItem {
  using EnumerableMap for EnumerableMap.UintToIntMap;
  using StructLib for EnumerableMap.UintToIntMap;

  // ---- CONSTANTS ----

  /// @notice Version of the contract
  /// @dev Should be incremented when contract changed
  string public constant CONSUMABLE_ITEM_BASE_VERSION = "1.0.1";

  // ---- VARIABLES ----

  bool internal _consumableItemInited;
  mapping(uint => EnumerableMap.UintToIntMap) internal _itemsConsumableAttributesPositive;
  mapping(uint => EnumerableMap.UintToIntMap) internal _itemsConsumableAttributesNegative;
  mapping(uint => IStatController.ChangeableStats) internal _itemsConsumableStats;
  GenerateConsumableInfo internal _consumableGenerateInfo;
  IStatController.ChangeableStats internal _consumableStatsInfo;

  /// @dev This empty reserved space is put in place to allow future versions to add new
  ///      variables without shifting down storage in the inheritance chain.
  ///      See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
  uint256[44] private __gap;

  // ---- EVENTS ----

  event Used(uint id, address hero, uint heroId);
  event ConsumableInfoChanged(GenerateConsumableInfo info, IStatController.ChangeableStats stats);

  // ---- INITIALIZER ----

  // init implementation
  constructor() {_consumableItemInited = true;}

  // ---- VIEWS ----

  function isConsumableItem() public pure override returns (bool) {
    return true;
  }

  function consumableAttributesAndStats(uint tokenId)
  external view override returns (uint[] memory positive, uint[] memory negative, IStatController.ChangeableStats memory stats) {
    return (_itemsConsumableAttributesPositive[tokenId].flat(), _itemsConsumableAttributesNegative[tokenId].flat(), _itemsConsumableStats[tokenId]);
  }

  // ---- GOV ACTIONS ----

  function setConsumableInfo(GenerateConsumableInfo memory consumableGenerateInfo_, IStatController.ChangeableStats memory consumableStatsInfo_) external {
    onlyDeployer();

    _consumableGenerateInfo = consumableGenerateInfo_;
    _consumableStatsInfo = consumableStatsInfo_;

    _consumableItemInited = true;

    emit ConsumableInfoChanged(consumableGenerateInfo_, consumableStatsInfo_);
  }

  // ---- ACTIONS ----

  function use(uint tokenId, address heroToken, uint heroTokenId) external {
    IController c = IController(controller());
    onlyOwner(tokenId);
    require(_isNotSmartContract(), "EOA");
    require(!c.onPause(), 'paused');

    IStatController _statController = IStatController(c.statController());
    ItemLib.checkRequirements(_statController, heroToken, heroTokenId, _requirements);

    _statController.registerConsumableUsage(heroToken, heroTokenId, address(this));

    _statController.changeCurrentStats(heroToken, heroTokenId, _itemsConsumableStats[tokenId], true);

    _statController.changeBonusAttributes(IStatController.ChangeAttributesInfo({
      heroToken: heroToken,
      heroTokenId: heroTokenId,
      changeAttributes: _itemsConsumableAttributesPositive[tokenId].flat(),
      increase: true,
      temporally: true
    }));

    _statController.changeBonusAttributes(IStatController.ChangeAttributesInfo({
      heroToken: heroToken,
      heroTokenId: heroTokenId,
      changeAttributes: _itemsConsumableAttributesNegative[tokenId].flat(),
      increase: false,
      temporally: true
    }));


    _destroy(tokenId);
    emit Used(tokenId, heroToken, heroTokenId);
  }

  function _afterMint(uint tokenId) internal override {
    super._afterMint(tokenId);
    _itemsConsumableStats[tokenId] = _generateConsumableItem(_itemsConsumableAttributesPositive[tokenId], _itemsConsumableAttributesNegative[tokenId], tokenId);
  }

  function _generateConsumableItem(EnumerableMap.UintToIntMap storage attributesPos, EnumerableMap.UintToIntMap storage attributesNeg, uint /*tokenId*/) internal virtual returns (IStatController.ChangeableStats memory) {
    IItemCalculator itemCalculator = IItemCalculator(IController(controller()).itemCalculator());

    (IItem.GenerateInfo memory posInfo, IItem.GenerateInfo memory negInfo) = itemCalculator.consumableInfoToGenerateInfo(_consumableGenerateInfo);

    (uint[] memory idsPos, uint[] memory valuesPos,) = itemCalculator.generateAttributes(posInfo, defaultRarity);
    for (uint i; i < valuesPos.length;) {
      uint value = valuesPos[i];
      uint index = idsPos[i];
      if (value != 0) {
        attributesPos.setUint(index, value);
      }
      unchecked{++i;}
    }

    (uint[] memory idsNeg, uint[] memory valuesNeg,) = itemCalculator.generateAttributes(negInfo, defaultRarity);

    for (uint i; i < valuesNeg.length;) {
      uint value = valuesNeg[i];
      uint index = idsNeg[i];
      if (value != 0) {
        attributesNeg.setUint(index, value);
      }
      unchecked{++i;}
    }

    return _consumableStatsInfo;
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;

import "../NftBase.sol";
import "../../openzeppelin/IERC20.sol";
import "../../openzeppelin/SafeERC20.sol";
import "../../openzeppelin/Math.sol";
import "../../interfaces/IItem.sol";
import "../../interfaces/IOracle.sol";
import "../../interfaces/IHero.sol";
import "../../interfaces/IGameToken.sol";
import "../../interfaces/IStatController.sol";
import "../../lib/StatLib.sol";
import "../../lib/StructLib.sol";
import "../../lib/ItemLib.sol";
import "../../interfaces/IItemCalculator.sol";
import "../../interfaces/IReinforcementController.sol";
import "../../interfaces/IChamberController.sol";
import "../../interfaces/ITreasury.sol";

abstract contract ItemBase is NftBase, IItem {
  using SafeERC20 for IERC20;
  using EnumerableMap for EnumerableMap.UintToIntMap;
  using StructLib for EnumerableMap.UintToIntMap;

  // ---- CONSTANTS ----

  /// @notice Version of the contract
  /// @dev Should be incremented when contract changed
  string public constant ITEM_BASE_VERSION = "1.0.3";

  // ---- VARIABLES ----

  bool internal _itemInited;
  address public override augmentToken;
  uint public override augmentTokenAmount;
  uint public override itemLevel;
  uint public override itemType;
  uint public override baseDurability;

  mapping(uint => EnumerableMap.UintToIntMap) internal _itemAttributes;
  mapping(uint => EnumerableMap.UintToIntMap) internal _negativeItemAttributes;
  mapping(uint => uint) public override itemRarity;
  mapping(uint => bool) public override equipped;
  mapping(uint => uint) public override augmentationLevel;
  mapping(uint => string) internal _itemUriByRarity;
  mapping(uint => uint) public override itemDurability;
  GenerateInfo internal _generateInfo;
  GenerateInfo internal _negativeGenerateInfo;
  IStatController.CoreAttributes internal _requirements;
  uint internal defaultRarity;
  /// @dev itemId => heroAdr => heroId
  mapping(uint => mapping(address => uint)) public override equippedOn;

  /// @dev This empty reserved space is put in place to allow future versions to add new
  ///      variables without shifting down storage in the inheritance chain.
  ///      See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
  uint256[36] private __gap;

  // ---- EVENTS ----

  event UriByRarityChanged(string uri, uint rarity);
  event Minted(uint id, uint rarity);
  event Equipped(uint tokenId, address heroToken, uint heroTokenId, uint itemSlot);
  event TakenOff(
    uint tokenId,
    address heroToken,
    uint heroTokenId,
    uint itemSlot,
    address destination
  );
  event Augmented(uint id, uint consumedId, uint augmentationLevel);
  event NotAugmented(uint id, uint consumedId, uint augmentationLevel);
  event Destroyed(uint id);
  event ItemRepaired(uint tokenId, uint consumedItemId);
  event RequirementsChanged(IStatController.CoreAttributes requirements);
  event GenInfoChanged(GenerateInfo info, GenerateInfo negativeInfo);
  event ReduceDurability(uint id, uint newDurability);
  event BaseDurabilityChanged(uint value);

  // ---- INITIALIZER ----

  // init implementation
  constructor() {_itemInited = true;}

  function initItem(
    address controller_,
    string memory name_,
    string memory symbol_,
    string memory uri_,
    address augmentToken_,
    uint augmentTokenAmount_,
  // Level in range 1-99. Reducing durability in low level dungeons. lvl/5+1 = biome
    uint itemLevel_,
    ItemType itemType_,
    uint baseDurability_
  ) external {
    _initializer();

    __NftBase_init(name_, symbol_, controller_, uri_);
    augmentToken = augmentToken_;
    augmentTokenAmount = augmentTokenAmount_;
    itemLevel = itemLevel_;
    itemType = uint(itemType_);
    baseDurability = baseDurability_;

    _finishInitializer();
  }

  // ---- RESTRICTIONS ----

  function onlyDungeon() internal view {
    require(IController(controller()).validDungeons(msg.sender), "!dungeon");
  }

  function onlyOwner(uint tokenId) internal view {
    require(ownerOf(tokenId) == _msgSender(), "forbidden");
  }

  function onlyHeroOrValidContract(IController c) internal view {
    require(c.validHeroes(msg.sender) || c.storyController() == msg.sender, "forbidden");
  }

  function _beforeTokenTransfer(
    address from,
    address to,
    uint256 tokenId
  ) internal override {
    require(!equipped[tokenId], "equipped");
    super._beforeTokenTransfer(from, to, tokenId);
  }

  // ---- VIEWS ----

  function isOwner(address spender, uint tokenId) external view override returns (bool) {
    return ownerOf(tokenId) == spender;
  }

  function isItem() external pure override returns (bool) {
    return true;
  }

  function isAttackItem() external pure override virtual returns (bool) {
    return false;
  }

  function isBuffItem() external pure override virtual returns (bool) {
    return false;
  }

  function isConsumableItem() public pure virtual override returns (bool) {
    return false;
  }

  function itemAttributes(uint tokenId) external view override returns (uint[] memory) {
    return _itemAttributes[tokenId].flat();
  }

  function negativeItemAttributes(uint tokenId) external view override returns (uint[] memory) {
    return _negativeItemAttributes[tokenId].flat();
  }

  function _specificURI(uint tokenId) internal view override returns (string memory) {
    return _itemUriByRarity[itemRarity[tokenId]];
  }

  function requirementAttributes() public view override returns (IStatController.CoreAttributes memory) {
    return _requirements;
  }

  function score(uint tokenId) external view override returns (uint) {
    return IItemCalculator(IController(controller()).itemCalculator()).score(_itemAttributes[tokenId].flat(), baseDurability);
  }

  // ---- GOV ACTIONS ----

  function setItemUriByRarity(string memory uri, uint rarity) external {
    onlyDeployer();
    _itemUriByRarity[rarity] = uri;
    emit UriByRarityChanged(uri, rarity);
  }

  function setItemMeta(
    GenerateInfo memory _info,
    GenerateInfo memory _negativeInfo,
    uint strengthReq,
    uint dexterityReq,
    uint vitalityReq,
    uint energyReq,
    uint defaultRarity_
  ) external {
    onlyDeployer();
    require(_info.minRandomAttributes < 2, "wrong min");

    _generateInfo = _info;
    _negativeGenerateInfo = _negativeInfo;
    _requirements = IStatController.CoreAttributes(
      strengthReq,
      dexterityReq,
      vitalityReq,
      energyReq
    );
    defaultRarity = defaultRarity_;

    _itemInited = true;

    emit RequirementsChanged(IStatController.CoreAttributes(
      strengthReq,
      dexterityReq,
      vitalityReq,
      energyReq
    ));
    emit GenInfoChanged(_info, _negativeInfo);
  }

  function setBaseDurability(uint value) external {
    onlyDeployer();
    baseDurability = value;
    emit BaseDurabilityChanged(value);
  }

  // ---- DUNGEON ACTIONS ----

  function mint() external override returns (uint tokenId) {
    onlyDungeon();
    return _mintInternal();
  }

  function _mintInternal() internal returns (uint tokenId) {
    tokenId = _incrementAndGetId();
    _safeMint(_msgSender(), tokenId);

    IItemCalculator itemCalculator = IItemCalculator(IController(controller()).itemCalculator());

    uint rarity = ItemLib.generateAttributes(itemCalculator, _itemAttributes[tokenId], _generateInfo, defaultRarity);
    ItemLib.generateAttributes(itemCalculator, _negativeItemAttributes[tokenId], _negativeGenerateInfo, 1);

    itemRarity[tokenId] = rarity;
    itemDurability[tokenId] = baseDurability;

    _afterMint(tokenId);
    emit Minted(tokenId, rarity);
  }

  // ---- EOA ACTIONS ----
  // an item must not be equipped

  function equip(uint tokenId, address heroToken, uint heroTokenId, uint itemSlot) external override {
    IController _controller = IController(controller());
    onlyOwner(tokenId);
    require(_isNotSmartContract(), "EOA");
    require(!equipped[tokenId], "equipped");
    require(!_controller.onPause(), 'paused');
    require(IHero(heroToken).payToken() != address(0), "forbidden");

    ItemLib.equip(
      _itemAttributes,
      _negativeItemAttributes,
      ItemLib.EquipContext({
        sender: _msgSender(),
        _controller: _controller,
        tokenId: tokenId,
        heroToken: heroToken,
        heroTokenId: heroTokenId,
        itemSlot: itemSlot,
        _itemType: itemType,
        _baseDurability: baseDurability,
        _itemDurability: itemDurability[tokenId],
        requirements: _requirements
      })
    );

    // transfer item to hero
    _safeTransfer(_msgSender(), heroToken, tokenId, '');
    // need to equip after transfer for properly checks
    equipped[tokenId] = true;
    equippedOn[tokenId][heroToken] = heroTokenId;
    emit Equipped(tokenId, heroToken, heroTokenId, itemSlot);
  }

  /// @dev Repair durability.
  function repairDurability(uint tokenId, uint consumedItemId) external override {
    IController c = IController(controller());
    require(_isNotSmartContract(), "EOA");
    onlyOwner(tokenId);
    onlyOwner(consumedItemId);
    uint _baseDurability = baseDurability;
    require(_baseDurability != 0, "!durability");
    require(!equipped[tokenId] && !equipped[consumedItemId], "equipped");
    require(!c.onPause(), 'paused');

    _destroy(consumedItemId);
    ItemLib.sendFee(IController(controller()), augmentToken, FeeType.REPAIR, _msgSender(), augmentTokenAmount / 10);
    itemDurability[tokenId] = _baseDurability;
    emit ItemRepaired(tokenId, consumedItemId);
  }


  function augment(uint tokenId, uint consumedItemId) external {
    require(_isNotSmartContract(), "EOA");
    onlyOwner(tokenId);
    onlyOwner(consumedItemId);
    require(!isConsumableItem(), "consumable");

    _destroy(consumedItemId);

    (bool success, uint agLevel) = ItemLib.augment(
      _msgSender(),
      augmentToken,
      augmentTokenAmount,
      tokenId,
      consumedItemId,
      augmentationLevel[tokenId],
      IController(controller()),
      equipped,
      _itemAttributes
    );

    if (success) {
      _additionalAugment(tokenId);
      augmentationLevel[tokenId] = agLevel;
      emit Augmented(tokenId, consumedItemId, agLevel);
    } else {
      _destroy(tokenId);
      emit NotAugmented(tokenId, consumedItemId, agLevel);
    }
  }

  // ---- HERO ACTIONS ----
  // assume the item is equipped to the hero and the hero is owner

  /// @dev Only hero contract can do it. Equipped items should be on hero.
  function takeOff(
    uint tokenId,
    address heroToken,
    uint heroTokenId,
    uint itemSlot,
    address destination,
    bool broken
  ) external override {
    IController c = IController(controller());
    onlyHeroOrValidContract(c);
    require(equipped[tokenId] && equippedOn[tokenId][heroToken] == heroTokenId, "!equipped");

    uint _itemType = itemType;
    require(_itemType != 0, "consumable");

    IStatController statController = IStatController(c.statController());

    statController.changeHeroItemSlot(
      heroToken,
      heroTokenId,
      _itemType,
      itemSlot,
      address(this),
      tokenId,
      false
    );

    if (broken) {
      itemDurability[tokenId] = 0;
    }

    statController.changeBonusAttributes(IStatController.ChangeAttributesInfo({
      heroToken: heroToken,
      heroTokenId: heroTokenId,
      changeAttributes: _itemAttributes[tokenId].flat(),
      increase: false,
      temporally: false
    }));

    statController.changeBonusAttributes(IStatController.ChangeAttributesInfo({
      heroToken: heroToken,
      heroTokenId: heroTokenId,
      changeAttributes: _negativeItemAttributes[tokenId].flat(),
      increase: true,
      temporally: false
    }));

    // need to take off before transfer for properly checks
    equipped[tokenId] = false;
    equippedOn[tokenId][heroToken] = 0;
    _safeTransfer(heroToken, destination, tokenId, '');
    emit TakenOff(tokenId, heroToken, heroTokenId, itemSlot, destination);
  }

  /// @dev Only hero contract can do it. Reduce after a battle.
  function reduceDurability(uint tokenId, uint dungeonBiomeLevel) external override returns (uint) {
    IController _controller = IController(controller());
    onlyHeroOrValidContract(_controller);

    IItemCalculator itemCalculator = IItemCalculator(_controller.itemCalculator());
    uint newDurability = itemCalculator.calcReduceDurability(dungeonBiomeLevel, itemDurability[tokenId], itemLevel, itemType);
    itemDurability[tokenId] = newDurability;
    emit ReduceDurability(tokenId, newDurability);
    return newDurability;
  }

  /// @dev Some stories can destroy items
  function destroy(uint tokenId) external override {
    IController c = IController(controller());
    require(IChamberController(c.chamberController()).validChambers(msg.sender)
    || c.storyController() == msg.sender
    || ownerOf(tokenId) == msg.sender
      , 'forbidden');
    require(!equipped[tokenId], "equipped");

    _destroy(tokenId);
  }

  function _destroy(uint tokenId) internal {
    _burn(tokenId);
    emit Destroyed(tokenId);
  }

  function _afterMint(uint tokenId) internal virtual {}

  function _additionalAugment(uint tokenId) internal virtual {}

}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;

import "../openzeppelin/ERC721EnumerableUpgradeable.sol";
import "../proxy/Controllable.sol";

abstract contract NftBase is ERC721EnumerableUpgradeable, Controllable {

  // ---- CONSTANTS ----

  /// @notice Version of the contract
  /// @dev Should be incremented when contract changed
  string public constant NFT_BASE_VERSION = "1.0.1";

  // ---- VARIABLES ----
  uint internal idCounter = 1;
  string internal __baseUri;
  mapping(uint => string) internal _uniqueUri;

  /// @dev This empty reserved space is put in place to allow future versions to add new
  ///      variables without shifting down storage in the inheritance chain.
  ///      See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
  uint256[47] private __gap;

  // ---- EVENTS ----

  event UniqueUriChanged(uint id, string uri);
  event BaseUriChanged(string uri);

  // ---- INITIALIZER ----

  function __NftBase_init(
    string memory name_,
    string memory symbol_,
    address controller_,
    string memory uri
  ) internal {
    _onlyInitializing();
    __ERC721_init(name_, symbol_);
    __Controllable_init(controller_);
    idCounter = 1;
    __baseUri = uri;
    emit BaseUriChanged(uri);
  }

  function _incrementAndGetId() internal returns (uint){
    uint id = idCounter;
    idCounter = id + 1;
    return id;
  }

  function onlyDeployer() internal view {
    require(IController(controller()).isDeployer(msg.sender), "!deployer");
  }

  // ---- VIEWS ----

  function _baseURI() internal view override returns (string memory) {
    return __baseUri;
  }

  function exists(uint tokenId) external view returns (bool) {
    return _exists(tokenId);
  }

  function tokenURI(uint256 tokenId) public view override returns (string memory) {
    require(tokenId <= idCounter, "!exist");

    // unique uri used for concrete tokenId
    string memory uniqueURI = _uniqueUri[tokenId];
    if (bytes(uniqueURI).length != 0) {
      return uniqueURI;
    }

    // specific token uri used for group of ids based on nft internal logic (such as item rarity)
    string memory specificURI = _specificURI(tokenId);
    if (bytes(specificURI).length > 0) {
      return specificURI;
    }
    return _baseURI();
  }

  function _specificURI(uint) internal view virtual returns (string memory) {
    return "";
  }

  function baseURI() external view returns (string memory) {
    return _baseURI();
  }

  // ---- GOV ACTIONS ----

  function setUniqueUri(uint tokenId, string memory uri) external {
    onlyDeployer();
    _uniqueUri[tokenId] = uri;
    emit UniqueUriChanged(tokenId, uri);
  }

  function setBaseUri(string memory value) external {
    onlyDeployer();
    __baseUri = value;
    emit BaseUriChanged(value);
  }

}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;

import "./IStatController.sol";

interface IAttackItem {

  struct GenerateAttackInfo {
    IStatController.MagicAttackType attackType;
    IStatController.CoreAttributes attributeFactors;
    uint damageMin;
    uint damageMax;
    uint manaConsumeMin;
    uint manaConsumeMax;
  }

  function attackAttributes(uint tokenId) external view returns (IStatController.MagicAttack memory);

}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;

import "./IStatController.sol";

interface IBuffItem {

  function buff(uint tokenId)
  external view returns (uint[] memory casterBuff, uint[] memory casterDebuff, uint mana);

  function debuff(uint tokenId)
  external view returns (uint[] memory targetDebuff, uint[] memory targetBuff);

}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;

interface IChamberController {

  enum ChamberType {
    UNKNOWN_0, // 0
    ENEMY_NPC_1, // 1
    ENEMY_NPC_SUPER_RARE_2, // 2
    BOSS_3, // 3
    SHRINE_4, // 4
    CHEST_5, // 5
    STORY_6, // 6
    STORY_UNIQUE_7, // 7
    SHRINE_UNIQUE_8, // 8
    CHEST_UNIQUE_9, // 9
    ENEMY_NPC_UNIQUE_10, // 10
    STORY_ON_ROAD_11, // 11
    STORY_UNDERGROUND_12, // 12
    STORY_NIGHT_CAMP_13, // 13
    STORY_MOUNTAIN_14, // 14
    STORY_WATER_15, // 15
    STORY_CASTLE_16, // 16
    STORY_HELL_17, // 17
    STORY_SPACE_18, // 18
    STORY_WOOD_19, // 19
    STORY_CATACOMBS_20, // 20
    STORY_BAD_HOUSE_21, // 21
    STORY_GOOD_TOWN_22, // 22
    STORY_BAD_TOWN_23, // 23
    STORY_BANDIT_CAMP_24, // 24
    STORY_BEAST_LAIR_25, // 25
    STORY_PRISON_26, // 26
    STORY_SWAMP_27, // 27
    STORY_INSIDE_28, // 28
    STORY_OUTSIDE_29, // 29
    STORY_INSIDE_RARE_30,
    STORY_OUTSIDE_RARE_31,
    ENEMY_NPC_INSIDE_32,
    ENEMY_NPC_INSIDE_RARE_33,
    ENEMY_NPC_OUTSIDE_34,
    ENEMY_NPC_OUTSIDE_RARE_35,
    SLOT_36,
    SLOT_37,
    SLOT_38,
    SLOT_39,
    SLOT_40,
    SLOT_41,
    SLOT_42,
    SLOT_43,
    SLOT_44,
    SLOT_45,
    SLOT_46,
    SLOT_47,
    SLOT_48,
    SLOT_49,
    SLOT_50
  }

  function validChambers(address chamber) external view returns (bool);

  function chambersByTypeAndBiomeLevel(uint cType, uint biome, uint index) external view returns (address);

  function chambersByTypeAndBiomeLevelLength(uint cType, uint biome) external view returns (uint);

  function getRandomChamber(uint[] memory cTypes, uint[] memory chances, uint biomeLevel, address heroToken, uint heroTokenId) external returns (address);

}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;

import "./IStatController.sol";

interface IConsumableItem {

  struct GenerateConsumableInfo {
    uint[] ids;
    uint[] values;
    bool[] increase;
  }

  function consumableAttributesAndStats(uint tokenId)
  external view returns (uint[] memory positive, uint[] memory negative, IStatController.ChangeableStats memory stats);

}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;

interface IControllable {

  function VERSION() external pure returns (string memory);

  function revision() external view returns (uint);

  function previousImplementation() external view returns (address);

  function isController(address _contract) external view returns (bool);

  function isGovernance(address _contract) external view returns (bool);

  function created() external view returns (uint256);

  function createdBlock() external view returns (uint256);

  function controller() external view returns (address);

  function increaseRevision(address oldLogic) external;

}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;

interface IController {

  function dungeonSpecific(uint heroLvl, uint heroCls) external view returns (address);

  function governance() external view returns (address);

  function statController() external view returns (address);

  function storyController() external view returns (address);

  function chamberController() external view returns (address);

  function reinforcementController() external view returns (address);

  function oracle() external view returns (address);

  function treasury() external view returns (address);

  function fightCalculator() external view returns (address);

  function itemCalculator() external view returns (address);

  function dungeonFactory() external view returns (address);

  function gameToken() external view returns (address);

  function fightDelay() external view returns (uint);

  function dungeonMultiplier(address dungeonImpl, address monsterProxy) external view returns (uint);

  function validHeroes(address hero) external view returns (bool);

  function validDungeons(address dungeon) external view returns (bool);

  function validItems(address item) external view returns (bool);

  function validTreasuryTokens(address token) external view returns (bool);

  function heroes(uint id) external view returns (address);

  function heroNameExist(string memory name) external view returns (bool);

  function globalBiomeMonsterMultiplier(uint biome) external view returns (uint);

  function dungeons(uint id) external view returns (address);

  function items(uint id) external view returns (address);

  function heroesLength() external view returns (uint);

  function dungeonsLength() external view returns (uint);

  function itemsLength() external view returns (uint);

  function dungeonImplByBiomeLevel(uint level, uint index) external view returns (address);

  function dungeonImplLength(uint level) external view returns (uint);

  function minLevelForTreasury(address token) external view returns (uint);

  function registerDungeon(address dungeonProxy) external;

  function registerHeroName(string memory name) external;

  function isDeployer(address adr) external view returns (bool);

  function onPause() external view returns (bool);

}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;

import "../openzeppelin/IERC20.sol";

interface IGameToken is IERC20 {

  function minter() external view returns (address);

  function mint(address account, uint amount) external returns (bool);

  function burn(uint amount) external returns (bool);

  function setMinter(address _minter) external;

  function pause(bool value) external;

}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;

import "./IStatController.sol";

interface IHero {

  struct TokenTreasury {
    address token;
    uint amount;
  }

  function attributes(uint tokenId) external view returns (uint[] memory);

  function lastFightTs(uint tokenId) external view returns (uint);

  function heroBiome(uint tokenId) external view returns (uint);

  function stats(uint tokenId) external view returns (IStatController.ChangeableStats memory);

  function currentDungeon(uint tokenId) external view returns (address);

  function isOwner(address account, uint256 tokenId) external view returns (bool);

  function isHero() external view returns (bool);

  function payToken() external view returns (address);

  function payTokenAmount() external view returns (uint);

  function heroClass() external view returns (uint);

  function score(uint tokenId) external view returns (uint);

  function heroReinforcementHelp(uint tokenId) external view returns (address heroToken, uint heroId);

  function isReadyToFight(uint tokenId) external view returns (bool);

  function isAlive(uint tokenId) external view returns (bool);

  function create(string memory name) external returns (uint);

  function kill(uint heroId) external returns (IStatController.NftItem[] memory dropItems, uint dropTokenAmount);

  function levelUp(uint tokenId, IStatController.CoreAttributes memory change) external;

  function reduceDurability(uint heroId, uint dungeonLevel) external;

  function changeCurrentDungeon(uint tokenId, address dungeon) external;

  function refreshLastFight(uint tokenId) external;

  function changeCurrentStats(
    uint tokenId,
    IStatController.ChangeableStats memory change,
    bool increase
  ) external;

  function tokenTreasures(uint tokenId) external view returns (uint);

  function releaseReinforcement(uint heroId) external returns (address helperToken, uint helperId);

}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;

import "./IStatController.sol";

interface IItem {

  struct GenerateInfo {
    uint[] ids;
    uint[] mins;
    uint[] maxs;
    uint[] chances;
    // it doesn't include positions with 100% chance
    uint minRandomAttributes;
    uint maxRandomAttributes;
  }

  enum FeeType {
    UNKNOWN,
    REPAIR,
    AUGMENT
  }

  enum ItemRarity {
    UNKNOWN, // 0
    NORMAL, // 1
    MAGIC, // 2
    RARE, // 3
    SET, // 4
    UNIQUE // 5
  }

  enum ItemType {
    NO_SLOT, // 0
    HEAD, // 1
    BODY, // 2
    GLOVES, // 3
    BELT, // 4
    AMULET, // 5
    RING, // 6
    OFF_HAND, // 7
    BOOTS, // 8
    ONE_HAND, // 9
    TWO_HAND, // 10
    SKILL // 11
  }

  enum ItemMetaType {
    UNKNOWN, // 0
    COMMON, // 1
    ATTACK_ITEM, // 2
    BUFF_ITEM, // 3
    CONSUMABLE_ITEM // 4
  }

  struct ItemMeta {
    string name;
    string symbol;
    string uri;
    address augmentToken;
    uint augmentTokenAmount;
    // Level in range 1-99. Reducing durability in low level dungeons. lvl/5+1 = biome
    uint itemLevel;
    ItemType itemType;
    uint baseDurability;
  }

  function itemMetaType() external view returns (ItemMetaType);

  function isOwner(address account, uint256 tokenId) external view returns (bool);

  function augmentationLevel(uint tokenId) external view returns (uint);

  function itemRarity(uint tokenId) external view returns (uint);

  function baseDurability() external view returns (uint);

  function itemDurability(uint tokenId) external view returns (uint);

  function equipped(uint tokenId) external view returns (bool);

  function equippedOn(uint tokenId, address heroAdr) external view returns (uint heroId);

  function isItem() external pure returns (bool);

  function isAttackItem() external view returns (bool);

  function isBuffItem() external view returns (bool);

  function isConsumableItem() external pure returns (bool);

  function augmentToken() external returns (address);

  function augmentTokenAmount() external returns (uint);

  function itemLevel() external returns (uint);

  function itemType() external returns (uint);

  function itemAttributes(uint tokenId) external view returns (uint[] memory);

  function negativeItemAttributes(uint tokenId) external view returns (uint[] memory);

  function requirementAttributes() external returns (IStatController.CoreAttributes memory);

  function score(uint tokenId) external view returns (uint);

  function mint() external returns (uint tokenId);

  function equip(uint tokenId, address heroToken, uint heroTokenId, uint itemSlot) external;

  function takeOff(uint tokenId, address heroToken, uint heroTokenId, uint itemSlot, address destination, bool broken) external;

  function reduceDurability(uint tokenId, uint dungeonBiomeLevel) external returns (uint);

  function repairDurability(uint tokenId, uint consumedItemId) external;

  function destroy(uint tokenId) external;
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;

import "./IItem.sol";
import "./IBuffItem.sol";
import "./IAttackItem.sol";
import "./IStatController.sol";
import "./IConsumableItem.sol";

interface IItemCalculator {

  function augmentGovFee() external view returns (uint);

  function repairGovFee() external view returns (uint);

  function score(uint[] memory attributes, uint baseDurability) external view returns (uint);

  function calcReduceDurability(
    uint dungeonBiomeLevel,
    uint currentDurability,
    uint _itemLevel,
    uint _itemType
  ) external pure returns (uint);

  function augmentSuccess(uint random) external view returns (bool);

  function generateAttributes(IItem.GenerateInfo memory _info, uint rarity) external returns (uint[] memory ids, uint[] memory values, IItem.ItemRarity itemRarity);

  function generateSkillAttributes(IItem.GenerateInfo memory _info, bool onlyPositiveChances)
  external returns (uint[] memory ids, uint[] memory values);

  function generateAttack(IAttackItem.GenerateAttackInfo memory _info) external returns (IStatController.MagicAttack memory);

  function augmentAttribute(uint value) external view returns (uint);

  function augmentMagicAttack(IStatController.MagicAttack memory current) external pure returns (IStatController.MagicAttack memory);

  function consumableInfoToGenerateInfo(IConsumableItem.GenerateConsumableInfo memory consumableGenInfo) external pure returns (IItem.GenerateInfo memory pos, IItem.GenerateInfo memory neg);

}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;

interface IOracle {

  function getRandomNumber(uint max, uint seed) external returns (uint);

  function getRandomNumberInRange(uint min, uint max, uint seed) external returns (uint);

}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;

import "./IStatController.sol";

interface IReinforcementController {

  function toHelperRatio(address heroToken, uint heroId) external view returns (uint);

  function isStaked(address heroToken, uint heroId) external view returns (bool);

  function askHero(uint biome) external returns (address heroToken, uint heroId, uint[] memory attributes);

  function registerTokenReward(address heroToken, uint heroId, address token, uint amount) external;

  function registerNftReward(address heroToken, uint heroId, address token, uint tokenId) external;

}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;

interface IStatController {

  enum ATTRIBUTES {
    // core
    STRENGTH, // 0
    DEXTERITY, // 1
    VITALITY, // 2
    ENERGY, // 3
    // attributes
    DAMAGE_MIN, // 4
    DAMAGE_MAX, // 5
    ATTACK_RATING, // 6
    DEFENSE, // 7
    BLOCK_RATING, // 8
    LIFE, // 9
    MANA, // 10
    // resistance
    FIRE_RESISTANCE, // 11
    COLD_RESISTANCE, // 12
    LIGHTNING_RESISTANCE, // 13
    // dmg against
    DMG_AGAINST_HUMAN, // 14
    DMG_AGAINST_UNDEAD, // 15
    DMG_AGAINST_DAEMON, // 16
    DMG_AGAINST_BEAST, // 17

    // defence against
    DEF_AGAINST_HUMAN, // 18
    DEF_AGAINST_UNDEAD, // 19
    DEF_AGAINST_DAEMON, // 20
    DEF_AGAINST_BEAST, // 21

    // --- unique, not augmentable
    // hero will not die until have positive chances
    LIFE_CHANCES, // 22
    // increase chance to get an item
    MAGIC_FIND, // 23
    // decrease chance to get an item
    DESTROY_ITEMS, // 24
    // percent of chance x2 dmg
    CRITICAL_HIT, // 25
    // dmg factors
    MELEE_DMG_FACTOR, // 26
    FIRE_DMG_FACTOR, // 27
    COLD_DMG_FACTOR, // 28
    LIGHTNING_DMG_FACTOR, // 29
    // increase attack rating on given percent
    AR_FACTOR, // 30
    // percent of damage will be converted to HP
    LIFE_STOLEN_PER_HIT, // 31
    // amount of mana restored after each battle
    MANA_AFTER_KILL, // 32
    // reduce all damage on percent after all other reductions
    DAMAGE_REDUCTION, // 33

    // -- statuses
    // chance to stun an enemy, stunned enemy skip next hit
    STUN, // 34
    // chance burn an enemy, burned enemy will loss 50% of defence
    BURN, // 35
    // chance freeze an enemy, frozen enemy will loss 50% of MELEE damage
    FREEZE, // 36
    // chance to reduce enemy's attack rating on 50%
    CONFUSE, // 37
    // chance curse an enemy, cursed enemy will loss 50% of resistance
    CURSE, // 38
    // percent of dmg return to attacker
    REFLECT_DAMAGE_MELEE, // 39
    REFLECT_DAMAGE_MAGIC, // 40
    // chance to poison enemy, poisoned enemy will loss 10% of the current health
    POISON, // 41
    // reduce chance get any of uniq statuses
    RESIST_TO_STATUSES, // 42

    END_SLOT // 46
  }

  // possible
  // HEAL_FACTOR

  struct CoreAttributes {
    uint strength;
    uint dexterity;
    uint vitality;
    uint energy;
  }

  struct ChangeableStats {
    uint level;
    uint experience;
    uint life;
    uint mana;
    uint lifeChances;
  }

  enum MagicAttackType {
    UNKNOWN, // 0
    FIRE, // 1
    COLD, // 2
    LIGHTNING, // 3
    CHAOS // 4
  }

  struct MagicAttack {
    MagicAttackType aType;
    uint min;
    uint max;
    // if not zero - activate attribute factor for the attribute
    CoreAttributes attributeFactors;
    uint manaConsume;
  }

  enum ItemSlots {
    UNKNOWN, // 0
    HEAD, // 1
    BODY, // 2
    GLOVES, // 3
    BELT, // 4
    AMULET, // 5
    BOOTS, // 6
    RIGHT_RING, // 7
    LEFT_RING, // 8
    RIGHT_HAND, // 9
    LEFT_HAND, // 10
    TWO_HAND, // 11
    SKILL_1, // 12
    SKILL_2, // 13
    SKILL_3, // 14
    END_SLOT // 15
  }

  struct NftItem {
    address token;
    uint tokenId;
  }

  enum Race {
    UNKNOWN, // 0
    HUMAN, // 1
    UNDEAD, // 2
    DAEMON, // 3
    BEAST, // 4
    SLOT_5, // 5
    SLOT_6, // 6
    SLOT_7, // 7
    SLOT_8, // 8
    SLOT_9, // 9
    SLOT_10 // 10
  }

  struct ChangeAttributesInfo {
    address heroToken;
    uint heroTokenId;
    uint[] changeAttributes;
    bool increase;
    bool temporally;
  }

  struct BuffInfo {
    address heroToken;
    uint heroTokenId;
    uint heroLevel;
    address[] buffTokens;
    uint[] buffTokenIds;
  }

  function initNewHero(address token, uint tokenId, uint heroClass) external;

  function heroAttributes(address token, uint tokenId) external view returns (uint[] memory);

  function heroAttribute(address token, uint tokenId, uint index) external view returns (uint);

  function heroAttributesLength(address token, uint tokenId) external view returns (uint);

  function heroBaseAttributes(address token, uint tokenId) external view returns (CoreAttributes memory);

  function heroCustomData(address token, uint tokenId, bytes32 index) external view returns (uint);

  function globalCustomData(bytes32 index) external view returns (uint);

  function heroStats(address token, uint tokenId) external view returns (ChangeableStats memory);

  function heroItemSlot(address token, uint tokenId, uint itemSlot) external view returns (NftItem memory);

  function heroItemSlots(address heroToken, uint heroTokenId) external view returns (uint[] memory);

  function isHeroAlive(address heroToken, uint heroTokenId) external view returns (bool);

  function levelUp(address token, uint tokenId, uint heroClass, CoreAttributes memory change) external;

  function changeHeroItemSlot(
    address heroToken,
    uint heroTokenId,
    uint itemType,
    uint itemSlot,
    address itemToken,
    uint itemTokenId,
    bool equip
  ) external;

  function changeCurrentStats(
    address token,
    uint tokenId,
    ChangeableStats memory change,
    bool increase
  ) external;

  function changeBonusAttributes(ChangeAttributesInfo memory info) external;

  function registerConsumableUsage(address heroToken, uint heroTokenId, address item) external;

  function clearUsedConsumables(address heroToken, uint heroTokenId) external;

  function clearTemporallyAttributes(address heroToken, uint heroTokenId) external;

  function buffHero(BuffInfo memory info) external view returns (uint[] memory attributes, uint manaConsumed);

  function generateMonsterAttributes(
    uint[] memory ids,
    uint[] memory values,
    uint amplifier,
    uint dungeonMultiplier,
    uint baseExperience
  ) external pure returns (uint[] memory attributes, uint experience);

  function setHeroCustomData(address token, uint tokenId, bytes32 index, uint value) external;

  function setGlobalCustomData(bytes32 index, uint value) external;

}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;

import "./IItem.sol";

interface ITreasury {

  function balanceOfToken(address token) external view returns (uint);

  function sendToDungeon(address dungeon, address token, uint amount) external;

  function sendFee(address token, uint amount, IItem.FeeType feeType) external;

}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;

library CalcLib {

  function toUint(int n) internal pure returns (uint) {
    if (n < 0) {
      return 0;
    }
    return uint(n);
  }

  /// @dev Simplified pseudo-random for minor functionality
  function pseudoRandom(uint maxValue) internal view returns (uint) {
    if (maxValue == 0) {
      return 0;
    }
    // pseudo random number
    return (uint(keccak256(abi.encodePacked(blockhash(block.number), block.coinbase, block.difficulty, block.number, block.timestamp, tx.gasprice, gasleft()))) % (maxValue + 1));
  }

  /// @dev Simplified pseudo-random for minor functionality
  function pseudoRandomWithSeed(uint maxValue, uint seed) internal view returns (uint) {
    if (maxValue == 0) {
      return 0;
    }
    // pseudo random number
    return (uint(keccak256(abi.encodePacked(blockhash(block.number), block.coinbase, block.difficulty, block.number, block.timestamp, tx.gasprice, gasleft(), seed))) % (maxValue + 1));
  }

  /// @dev Simplified pseudo-random for minor functionality, in range
  function pseudoRandomInRange(uint min, uint max) internal view returns (uint) {
    if (min >= max) {
      return max;
    }
    uint r = pseudoRandom(max - min);
    return min + r;
  }

  function minusWithZeroFloor(uint a, uint b) internal pure returns (uint){
    if (a <= b) {
      return 0;
    }
    return a - b;
  }

  function sqrt(uint x) internal pure returns (uint z) {
    assembly {
    // Start off with z at 1.
      z := 1

    // Used below to help find a nearby power of 2.
      let y := x

    // Find the lowest power of 2 that is at least sqrt(x).
      if iszero(lt(y, 0x100000000000000000000000000000000)) {
        y := shr(128, y) // Like dividing by 2 ** 128.
        z := shl(64, z) // Like multiplying by 2 ** 64.
      }
      if iszero(lt(y, 0x10000000000000000)) {
        y := shr(64, y) // Like dividing by 2 ** 64.
        z := shl(32, z) // Like multiplying by 2 ** 32.
      }
      if iszero(lt(y, 0x100000000)) {
        y := shr(32, y) // Like dividing by 2 ** 32.
        z := shl(16, z) // Like multiplying by 2 ** 16.
      }
      if iszero(lt(y, 0x10000)) {
        y := shr(16, y) // Like dividing by 2 ** 16.
        z := shl(8, z) // Like multiplying by 2 ** 8.
      }
      if iszero(lt(y, 0x100)) {
        y := shr(8, y) // Like dividing by 2 ** 8.
        z := shl(4, z) // Like multiplying by 2 ** 4.
      }
      if iszero(lt(y, 0x10)) {
        y := shr(4, y) // Like dividing by 2 ** 4.
        z := shl(2, z) // Like multiplying by 2 ** 2.
      }
      if iszero(lt(y, 0x8)) {
      // Equivalent to 2 ** z.
        z := shl(1, z)
      }

    // Shifting right by 1 is like dividing by 2.
      z := shr(1, add(z, div(x, z)))
      z := shr(1, add(z, div(x, z)))
      z := shr(1, add(z, div(x, z)))
      z := shr(1, add(z, div(x, z)))
      z := shr(1, add(z, div(x, z)))
      z := shr(1, add(z, div(x, z)))
      z := shr(1, add(z, div(x, z)))

    // Compute a rounded down version of z.
      let zRoundDown := div(x, z)

    // If zRoundDown is smaller, use it.
      if lt(zRoundDown, z) {
        z := zRoundDown
      }
    }
  }

  /*********************************************
 *              PRB-MATH                      *
 *   https://github.com/hifi-finance/prb-math *
 **********************************************/

  /// @notice Calculates the binary logarithm of x.
  ///
  /// @dev Based on the iterative approximation algorithm.
  /// https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation
  ///
  /// Requirements:
  /// - x must be greater than or equal to SCALE, otherwise the result would be negative.
  ///
  /// Caveats:
  /// - The results are nor perfectly accurate to the last decimal,
  ///   due to the lossy precision of the iterative approximation.
  ///
  /// @param x The unsigned 60.18-decimal fixed-point number for which
  ///           to calculate the binary logarithm.
  /// @return result The binary logarithm as an unsigned 60.18-decimal fixed-point number.
  function log2(uint256 x) internal pure returns (uint256 result) {
    require(x >= 1e18, "x too low");

    // Calculate the integer part of the logarithm
    // and add it to the result and finally calculate y = x * 2^(-n).
    uint256 n = mostSignificantBit(x / 1e18);

    // The integer part of the logarithm as an unsigned 60.18-decimal fixed-point number.
    // The operation can't overflow because n is maximum 255 and SCALE is 1e18.
    uint256 rValue = n * 1e18;

    // This is y = x * 2^(-n).
    uint256 y = x >> n;

    // If y = 1, the fractional part is zero.
    if (y == 1e18) {
      return rValue;
    }

    // Calculate the fractional part via the iterative approximation.
    // The "delta >>= 1" part is equivalent to "delta /= 2", but shifting bits is faster.
    for (uint256 delta = 5e17; delta > 0; delta >>= 1) {
      y = (y * y) / 1e18;

      // Is y^2 > 2 and so in the range [2,4)?
      if (y >= 2 * 1e18) {
        // Add the 2^(-m) factor to the logarithm.
        rValue += delta;

        // Corresponds to z/2 on Wikipedia.
        y >>= 1;
      }
    }
    return rValue;
  }

  /// @notice Finds the zero-based index of the first one in the binary representation of x.
  /// @dev See the note on msb in the "Find First Set"
  ///      Wikipedia article https://en.wikipedia.org/wiki/Find_first_set
  /// @param x The uint256 number for which to find the index of the most significant bit.
  /// @return msb The index of the most significant bit as an uint256.
  //noinspection NoReturn
  function mostSignificantBit(uint256 x) internal pure returns (uint256 msb) {
    if (x >= 2 ** 128) {
      x >>= 128;
      msb += 128;
    }
    if (x >= 2 ** 64) {
      x >>= 64;
      msb += 64;
    }
    if (x >= 2 ** 32) {
      x >>= 32;
      msb += 32;
    }
    if (x >= 2 ** 16) {
      x >>= 16;
      msb += 16;
    }
    if (x >= 2 ** 8) {
      x >>= 8;
      msb += 8;
    }
    if (x >= 2 ** 4) {
      x >>= 4;
      msb += 4;
    }
    if (x >= 2 ** 2) {
      x >>= 2;
      msb += 2;
    }
    if (x >= 2 ** 1) {
      // No need to shift x any more.
      msb += 1;
    }
  }

}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;

import "./StructLib.sol";
import "../interfaces/IItemCalculator.sol";
import "../interfaces/IController.sol";
import "../interfaces/IReinforcementController.sol";
import "../interfaces/IHero.sol";
import "../interfaces/ITreasury.sol";
import "../interfaces/IOracle.sol";
import "../openzeppelin/SafeERC20.sol";

library ItemLib {
  using SafeERC20 for IERC20;
  using EnumerableMap for EnumerableMap.UintToIntMap;
  using StructLib for EnumerableMap.UintToIntMap;

  function generateAttributes(
    IItemCalculator itemCalculator,
    EnumerableMap.UintToIntMap storage attributes,
    IItem.GenerateInfo memory info,
    uint _defaultRarity
  ) external returns (uint) {
    (uint[] memory ids, uint[] memory values, IItem.ItemRarity rarity) = itemCalculator.generateAttributes(info, _defaultRarity);
    for (uint i; i < values.length;) {
      uint value = values[i];
      uint index = ids[i];
      if (value != 0) {
        attributes.setUint(index, value);
      }
      unchecked{++i;}
    }
    return uint(rarity);
  }

  struct EquipContext {
    address sender;
    IController _controller;
    uint tokenId;
    address heroToken;
    uint heroTokenId;
    uint itemSlot;
    uint _itemType;
    uint _baseDurability;
    uint _itemDurability;
    IStatController.CoreAttributes requirements;
  }

  function equip(
    mapping(uint => EnumerableMap.UintToIntMap) storage _itemAttributes,
    mapping(uint => EnumerableMap.UintToIntMap) storage _negativeItemAttributes,
    EquipContext memory context
  ) external {

    require(IHero(context.heroToken).isOwner(context.sender, context.heroTokenId), "!owner");
    require(context._controller.validHeroes(context.heroToken), "forbidden");
    require(IHero(context.heroToken).currentDungeon(context.heroTokenId) == address(0), "in dungeon");
    require(!IReinforcementController(context._controller.reinforcementController()).isStaked(context.heroToken, context.heroTokenId), "staked");

    IStatController _statController = IStatController(context._controller.statController());
    _checkRequirements(_statController, context.heroToken, context.heroTokenId, context.requirements);

    require(context._itemType != 0, "consumable");
    require(context._baseDurability == 0 || context._itemDurability > 0, "broken");

    _statController.changeHeroItemSlot(
      context.heroToken,
      context.heroTokenId,
      context._itemType,
      context.itemSlot,
      address(this),
      context.tokenId,
      true
    );

    if (_itemAttributes[context.tokenId].length() != 0) {
      _statController.changeBonusAttributes(IStatController.ChangeAttributesInfo({
        heroToken: context.heroToken,
        heroTokenId: context.heroTokenId,
        changeAttributes: _itemAttributes[context.tokenId].flat(),
        increase: true,
        temporally: false
      }));

      require(_statController.heroStats(context.heroToken, context.heroTokenId).life > 0, "!life");
    }

    if (_negativeItemAttributes[context.tokenId].length() != 0) {
      _statController.changeBonusAttributes(IStatController.ChangeAttributesInfo({
        heroToken: context.heroToken,
        heroTokenId: context.heroTokenId,
        changeAttributes: _negativeItemAttributes[context.tokenId].flat(),
        increase: false,
        temporally: false
      }));
    }
  }

  function augment(
    address sender,
    address token,
    uint augmentTokenAmount,
    uint tokenId,
    uint consumedItemId,
    uint ag,
    IController _controller,
    mapping(uint => bool) storage equipped,
    mapping(uint => EnumerableMap.UintToIntMap) storage _itemAttributes
  ) external returns (bool success, uint agLevel){

    require(token != address(0), "!augmentation");
    require(!equipped[tokenId] && !equipped[consumedItemId], "equipped");
    require(!_controller.onPause(), 'paused');
    require(ag < 10, 'too high ag lvl');

    IItemCalculator itemCalculator = IItemCalculator(_controller.itemCalculator());
    sendFee(_controller, token, IItem.FeeType.AUGMENT, sender, augmentTokenAmount);

    success = itemCalculator.augmentSuccess(IOracle(_controller.oracle()).getRandomNumber(1e18, 0));
    if (!success) {
      return (false, ag);
    }

    EnumerableMap.UintToIntMap storage attributes = _itemAttributes[tokenId];
    uint length = attributes.length();
    for (uint i; i < length;) {
      (uint index, int valueI) = attributes._at(i);
      if (valueI > 0) {
        uint value = uint(valueI);
        attributes.setUint(index, itemCalculator.augmentAttribute(value));
      }
      unchecked{++i;}
    }

    return (true, ag + 1);
  }

  function checkRequirements(IStatController _statController, address heroToken, uint heroTokenId, IStatController.CoreAttributes memory requirements) external view {
    _checkRequirements(_statController, heroToken, heroTokenId, requirements);
  }

  function _checkRequirements(IStatController _statController, address heroToken, uint heroTokenId, IStatController.CoreAttributes memory requirements) internal view {
    IStatController.CoreAttributes memory attributes = _statController.heroBaseAttributes(heroToken, heroTokenId);
    require(requirements.strength <= attributes.strength
    && requirements.dexterity <= attributes.dexterity
    && requirements.vitality <= attributes.vitality
    && requirements.energy <= attributes.energy
      , "!attributes");
  }

  function sendFee(IController _controller, address token, IItem.FeeType feeType, address sender, uint amount) public {
    if (token != address(0)) {
      address treasury = _controller.treasury();
      IERC20(token).safeTransferFrom(
        sender,
        address(this),
        amount
      );
      IERC20(token).safeApprove(treasury, amount);
      ITreasury(treasury).sendFee(token, amount, feeType);
    }
  }

}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;

/// @title Library for setting / getting slot variables (used in upgradable proxy contracts)
/// @author bogdoslav
library SlotsLib {

  /// @notice Version of the contract
  /// @dev Should be incremented when contract changed
  string public constant SLOT_LIB_VERSION = "1.0.0";

  // ************* GETTERS *******************

  /// @dev Gets a slot as bytes32
  function getBytes32(bytes32 slot) internal view returns (bytes32 result) {
    assembly {
      result := sload(slot)
    }
  }

  /// @dev Gets a slot as an address
  function getAddress(bytes32 slot) internal view returns (address result) {
    assembly {
      result := sload(slot)
    }
  }

  /// @dev Gets a slot as uint256
  function getUint(bytes32 slot) internal view returns (uint result) {
    assembly {
      result := sload(slot)
    }
  }

  // ************* ARRAY GETTERS *******************

  /// @dev Gets an array length
  function arrayLength(bytes32 slot) internal view returns (uint result) {
    assembly {
      result := sload(slot)
    }
  }

  /// @dev Gets a slot array by index as address
  /// @notice First slot is array length, elements ordered backward in memory
  /// @notice This is unsafe, without checking array length.
  function addressAt(bytes32 slot, uint index) internal view returns (address result) {
    bytes32 pointer = bytes32(uint(slot) - 1 - index);
    assembly {
      result := sload(pointer)
    }
  }

  // ************* SETTERS *******************

  /// @dev Sets a slot with bytes32
  /// @notice Check address for 0 at the setter
  function set(bytes32 slot, bytes32 value) internal {
    assembly {
      sstore(slot, value)
    }
  }

  /// @dev Sets a slot with address
  /// @notice Check address for 0 at the setter
  function set(bytes32 slot, address value) internal {
    assembly {
      sstore(slot, value)
    }
  }

  /// @dev Sets a slot with uint
  function set(bytes32 slot, uint value) internal {
    assembly {
      sstore(slot, value)
    }
  }

}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;

import "../interfaces/IStatController.sol";
import "../interfaces/IHero.sol";
import "../openzeppelin/Math.sol";
import "./CalcLib.sol";
import "../openzeppelin/EnumerableMap.sol";
import "./StructLib.sol";

library StatLib {
  using StructLib for EnumerableMap.UintToIntMap;
  using EnumerableMap for EnumerableMap.UintToIntMap;

  /// @notice Version of the contract
  /// @dev Should be incremented when contract changed
  string public constant STAT_LIB_VERSION = "1.0.0";
  uint public constant MAX_LEVEL = 99;
  uint public constant BASE_EXPERIENCE = 100_000;
  uint public constant BIOME_LEVEL_STEP = 5;
  uint internal constant _MAX_AMPLIFIER = 1e18;

  struct BaseMultiplier {
    uint minDamage;
    uint maxDamage;
    uint attackRating;
    uint defense;
    uint blockRating;
    uint life;
    uint mana;
  }

  struct LevelUp {
    uint life;
    uint mana;
  }

  struct InitialHero {
    IStatController.CoreAttributes core;
    BaseMultiplier multiplier;
    LevelUp levelUp;
    uint baseLifeChances;
  }

  uint private constant _PRECISION = 1e18;

  // --------- BASE -----------

  // --- HERO 1 (Slave) ---

  function initialHero1() internal pure returns (InitialHero memory) {
    return InitialHero({
      core: IStatController.CoreAttributes({
      strength: 15,
      dexterity: 15,
      vitality: 30,
      energy: 10
    }),

      multiplier: BaseMultiplier({
      minDamage: 0.1e18,
      maxDamage: 0.2e18,
      attackRating: 2e18,
      defense: 2e18,
      blockRating: 0.1e18,
      life: 1.5e18,
      mana: 0.5e18
    }),

      levelUp: LevelUp({
      life: 2e18,
      mana: 1e18
    }),

      baseLifeChances: 5
    });
  }

  // --- HERO 2 (Spata) ---

  function initialHero2() internal pure returns (InitialHero memory) {
    return InitialHero({
      core: IStatController.CoreAttributes({
      strength: 30,
      dexterity: 5,
      vitality: 25,
      energy: 10
    }),

      multiplier: BaseMultiplier({
      minDamage: 0.15e18,
      maxDamage: 0.25e18,
      attackRating: 2e18,
      defense: 1e18,
      blockRating: 0.08e18,
      life: 1.3e18,
      mana: 0.5e18
    }),

      levelUp: LevelUp({
      life: 1.8e18,
      mana: 1e18
    }),

      baseLifeChances: 5
    });
  }

  // --- HERO 3 (Decidia) ---

  function initialHero3() internal pure returns (InitialHero memory) {
    return InitialHero({
      core: IStatController.CoreAttributes({
      strength: 10,
      dexterity: 15,
      vitality: 20,
      energy: 25
    }),

      multiplier: BaseMultiplier({
      minDamage: 0.1e18,
      maxDamage: 0.2e18,
      attackRating: 2e18,
      defense: 1e18,
      blockRating: 0.1e18,
      life: 1e18,
      mana: 2e18
    }),

      levelUp: LevelUp({
      life: 1.3e18,
      mana: 2e18
    }),

      baseLifeChances: 5
    });
  }

  // --- HERO 4 (Innatus) ---

  function initialHero4() internal pure returns (InitialHero memory) {
    return InitialHero({
      core: IStatController.CoreAttributes({
      strength: 15,
      dexterity: 25,
      vitality: 15,
      energy: 15
    }),

      multiplier: BaseMultiplier({
      minDamage: 0.1e18,
      maxDamage: 0.2e18,
      attackRating: 4e18,
      defense: 3e18,
      blockRating: 0.2e18,
      life: 1.2e18,
      mana: 1e18
    }),

      levelUp: LevelUp({
      life: 1.7e18,
      mana: 1.5e18
    }),

      baseLifeChances: 5
    });
  }

  // --- HERO 5 (F2P) ---

  function initialHero5() internal pure returns (InitialHero memory) {
    return InitialHero({
      core: IStatController.CoreAttributes({
      strength: 20,
      dexterity: 20,
      vitality: 20,
      energy: 10
    }),

      multiplier: BaseMultiplier({
      minDamage: 0.15e18,
      maxDamage: 0.25e18,
      attackRating: 3e18,
      defense: 2.5e18,
      blockRating: 0.15e18,
      life: 1.5e18,
      mana: 1.5e18
    }),

      levelUp: LevelUp({
      life: 1.5e18,
      mana: 1.5e18
    }),

      baseLifeChances: 1
    });
  }

  // ------

  function initialHero(uint heroClass) internal pure returns (InitialHero memory) {
    if (heroClass == 1) {
      return initialHero1();
    } else if (heroClass == 2) {
      return initialHero2();
    } else if (heroClass == 3) {
      return initialHero3();
    } else if (heroClass == 4) {
      return initialHero4();
    } else if (heroClass == 5) {
      return initialHero5();
    } else {
      revert("Unknown class");
    }
  }

  // --------- CALCULATIONS -----------

  function minDamage(uint strength, uint heroClass) internal pure returns (uint){
    return strength * initialHero(heroClass).multiplier.minDamage / _PRECISION;
  }

  function maxDamage(uint strength, uint heroClass) internal pure returns (uint){
    return strength * initialHero(heroClass).multiplier.maxDamage / _PRECISION;
  }

  function attackRating(uint dexterity, uint heroClass) internal pure returns (uint){
    return dexterity * initialHero(heroClass).multiplier.attackRating / _PRECISION;
  }

  function defense(uint dexterity, uint heroClass) internal pure returns (uint){
    return dexterity * initialHero(heroClass).multiplier.defense / _PRECISION;
  }

  function blockRating(uint dexterity, uint heroClass) internal pure returns (uint){
    return Math.min((dexterity * initialHero(heroClass).multiplier.blockRating / _PRECISION), 75);
  }

  function life(uint vitality, uint heroClass, uint level) internal pure returns (uint){
    return (vitality * initialHero(heroClass).multiplier.life / _PRECISION)
      + (level * initialHero(heroClass).levelUp.life / _PRECISION);
  }

  function mana(uint energy, uint heroClass, uint level) internal pure returns (uint){
    return (energy * initialHero(heroClass).multiplier.mana / _PRECISION)
      + (level * initialHero(heroClass).levelUp.mana / _PRECISION);
  }

  function lifeChances(uint heroClass, uint level) internal pure returns (uint){
    return initialHero(heroClass).baseLifeChances + (level / 49);
  }

  function levelExperience(uint level) internal pure returns (uint) {
    if (level == 0 || level >= MAX_LEVEL) {
      return 0;
    }
    return level * BASE_EXPERIENCE * (67e17 - CalcLib.log2((MAX_LEVEL - level + 2) * 1e18)) / 1e18;
  }

  function chanceToHit(
    uint attackersAttackRating,
    uint defendersDefenceRating,
    uint attackersLevel,
    uint defendersLevel,
    uint arFactor
  ) internal pure returns (uint) {
    attackersAttackRating += attackersAttackRating * arFactor / 100;
    uint x = Math.max(attackersAttackRating, 1);
    uint y = Math.max(attackersAttackRating + defendersDefenceRating, 1);
    uint z = attackersLevel;
    uint k = defendersLevel / 2;
    uint xy = x * 1e18 / y;
    uint zk = z * 1e18 / (attackersLevel + k);
    uint base = 2 * xy * zk / 1e18;
    return Math.max(Math.min(base, 0.95e18), 0.05e18);
  }

  function experienceToLvl(uint experience, uint startFromLevel) internal pure returns (uint level) {
    level = startFromLevel;
    for (; level < MAX_LEVEL;) {
      if (levelExperience(level) >= experience) {
        break;
      }
      unchecked{++level;}
    }
  }

  function expPerMonster(uint monsterExp, uint monsterRarity, uint heroExp, uint heroCurrentLvl, uint monsterBiome) internal pure returns (uint) {
    uint heroLvl = experienceToLvl(heroExp, heroCurrentLvl);
    uint heroBiome = heroLvl / StatLib.BIOME_LEVEL_STEP + 1;
    uint base = monsterExp + monsterExp * monsterRarity / _MAX_AMPLIFIER;

    // reduce exp if hero not in his biome
    if (heroBiome > monsterBiome) {
      base = base / (2 ** (heroBiome - monsterBiome));
    }
    return base;
  }

  function amplify(uint value, uint amplifier, uint dungeonMultiplier) internal pure returns (uint) {
    if (value == 0) {
      return 0;
    }
    return value + (value * amplifier / _MAX_AMPLIFIER) + (value * dungeonMultiplier / _MAX_AMPLIFIER);
  }

  function mintDropChance(uint baseChance, uint monsterRarity, uint monsterBiome, uint heroExp, uint heroCurrentLvl) internal pure returns (uint) {
    uint heroLvl = StatLib.experienceToLvl(heroExp, heroCurrentLvl);
    uint heroBiome = heroLvl / StatLib.BIOME_LEVEL_STEP + 1;
    uint chance = baseChance + baseChance * monsterRarity / _MAX_AMPLIFIER;

    // reduce chance if hero not in his biome
    if (heroBiome > monsterBiome) {
      chance = chance / (2 ** (heroBiome - monsterBiome));
    }
    return chance;
  }

  function initAttributes(
    EnumerableMap.UintToIntMap storage attributes,
    uint heroClass,
    uint level,
    IStatController.CoreAttributes memory base
  ) internal {

    attributes.setUint(uint(IStatController.ATTRIBUTES.STRENGTH), base.strength);
    attributes.setUint(uint(IStatController.ATTRIBUTES.DEXTERITY), base.dexterity);
    attributes.setUint(uint(IStatController.ATTRIBUTES.VITALITY), base.vitality);
    attributes.setUint(uint(IStatController.ATTRIBUTES.ENERGY), base.energy);

    attributes.setUint(uint(IStatController.ATTRIBUTES.DAMAGE_MIN), minDamage(base.strength, heroClass));
    attributes.setUint(uint(IStatController.ATTRIBUTES.DAMAGE_MAX), maxDamage(base.strength, heroClass));
    attributes.setUint(uint(IStatController.ATTRIBUTES.ATTACK_RATING), attackRating(base.dexterity, heroClass));
    attributes.setUint(uint(IStatController.ATTRIBUTES.DEFENSE), defense(base.dexterity, heroClass));
    attributes.setUint(uint(IStatController.ATTRIBUTES.BLOCK_RATING), blockRating(base.dexterity, heroClass));
    attributes.setUint(uint(IStatController.ATTRIBUTES.LIFE), life(base.vitality, heroClass, level));
    attributes.setUint(uint(IStatController.ATTRIBUTES.MANA), mana(base.energy, heroClass, level));
    attributes.setUint(uint(IStatController.ATTRIBUTES.LIFE_CHANCES), lifeChances(heroClass, level));
  }

  function updateCoreDependAttributesInMemory(uint[] memory attributes, uint[] memory bonus, uint heroClass, uint level) internal pure returns (uint[] memory) {
    uint strength = attributes[uint(IStatController.ATTRIBUTES.STRENGTH)];
    uint dexterity = attributes[uint(IStatController.ATTRIBUTES.DEXTERITY)];
    uint vitality = attributes[uint(IStatController.ATTRIBUTES.VITALITY)];
    uint energy = attributes[uint(IStatController.ATTRIBUTES.ENERGY)];

    attributes[uint(IStatController.ATTRIBUTES.DAMAGE_MIN)] = minDamage(strength, heroClass) + bonus[uint(IStatController.ATTRIBUTES.DAMAGE_MIN)];
    attributes[uint(IStatController.ATTRIBUTES.DAMAGE_MAX)] = maxDamage(strength, heroClass) + bonus[uint(IStatController.ATTRIBUTES.DAMAGE_MAX)];
    attributes[uint(IStatController.ATTRIBUTES.ATTACK_RATING)] = attackRating(dexterity, heroClass) + bonus[uint(IStatController.ATTRIBUTES.ATTACK_RATING)];
    attributes[uint(IStatController.ATTRIBUTES.DEFENSE)] = defense(dexterity, heroClass) + bonus[uint(IStatController.ATTRIBUTES.DEFENSE)];
    attributes[uint(IStatController.ATTRIBUTES.BLOCK_RATING)] = blockRating(dexterity, heroClass) + bonus[uint(IStatController.ATTRIBUTES.BLOCK_RATING)];
    attributes[uint(IStatController.ATTRIBUTES.LIFE)] = life(vitality, heroClass, level) + bonus[uint(IStatController.ATTRIBUTES.LIFE)];
    attributes[uint(IStatController.ATTRIBUTES.MANA)] = mana(energy, heroClass, level) + bonus[uint(IStatController.ATTRIBUTES.MANA)];
    return attributes;
  }

  function updateCoreDependAttributes(
    EnumerableMap.UintToIntMap storage attributes,
    EnumerableMap.UintToIntMap storage bonusMain,
    EnumerableMap.UintToIntMap storage bonusExtra,
    IStatController.ChangeableStats storage _heroStats,
    uint index,
    address heroToken,
    uint base
  ) internal {
    if (index == uint(IStatController.ATTRIBUTES.STRENGTH)) {
      uint heroClass = IHero(heroToken).heroClass();

      attributes.setUint(uint(IStatController.ATTRIBUTES.DAMAGE_MIN),
        StatLib.minDamage(base, heroClass)
        + bonusMain.getOrZeroA(IStatController.ATTRIBUTES.DAMAGE_MIN)
        + bonusExtra.getOrZeroA(IStatController.ATTRIBUTES.DAMAGE_MIN)
      );
      attributes.setUint(uint(IStatController.ATTRIBUTES.DAMAGE_MAX),
        StatLib.maxDamage(base, heroClass)
        + bonusMain.getOrZeroA(IStatController.ATTRIBUTES.DAMAGE_MAX)
        + bonusExtra.getOrZeroA(IStatController.ATTRIBUTES.DAMAGE_MAX)
      );
    } else if (index == uint(IStatController.ATTRIBUTES.DEXTERITY)) {
      uint heroClass = IHero(heroToken).heroClass();

      attributes.setUint(uint(IStatController.ATTRIBUTES.ATTACK_RATING),
        StatLib.attackRating(base, heroClass)
        + bonusMain.getOrZeroA(IStatController.ATTRIBUTES.ATTACK_RATING)
        + bonusExtra.getOrZeroA(IStatController.ATTRIBUTES.ATTACK_RATING)
      );

      attributes.setUint(uint(IStatController.ATTRIBUTES.DEFENSE),
        StatLib.defense(base, heroClass)
        + bonusMain.getOrZeroA(IStatController.ATTRIBUTES.DEFENSE)
        + bonusExtra.getOrZeroA(IStatController.ATTRIBUTES.DEFENSE)
      );

      attributes.setUint(uint(IStatController.ATTRIBUTES.BLOCK_RATING),
        StatLib.blockRating(base, heroClass)
        + bonusMain.getOrZeroA(IStatController.ATTRIBUTES.BLOCK_RATING)
        + bonusExtra.getOrZeroA(IStatController.ATTRIBUTES.BLOCK_RATING)
      );
    } else if (index == uint(IStatController.ATTRIBUTES.VITALITY)) {
      uint heroClass = IHero(heroToken).heroClass();
      uint level = _heroStats.level;

      attributes.setUint(uint(IStatController.ATTRIBUTES.LIFE),
        StatLib.life(base, heroClass, level)
        + bonusMain.getOrZeroA(IStatController.ATTRIBUTES.LIFE)
        + bonusExtra.getOrZeroA(IStatController.ATTRIBUTES.LIFE)
      );
    } else if (index == uint(IStatController.ATTRIBUTES.ENERGY)) {
      uint heroClass = IHero(heroToken).heroClass();
      uint level = _heroStats.level;

      attributes.setUint(uint(IStatController.ATTRIBUTES.MANA),
        StatLib.mana(base, heroClass, level)
        + bonusMain.getOrZeroA(IStatController.ATTRIBUTES.MANA)
        + bonusExtra.getOrZeroA(IStatController.ATTRIBUTES.MANA)
      );
    }
  }

  function attributesAdd(uint[] memory base, uint[] memory add) internal pure returns (uint[] memory) {
    unchecked{
      for (uint i; i < base.length; ++i) {
        base[i] += add[i];
      }
    }
    return base;
  }

  function attributesRemove(uint[] memory base, uint[] memory remove) internal pure returns (uint[] memory) {
    unchecked{
      for (uint i; i < base.length; ++i) {
        base[i] = CalcLib.minusWithZeroFloor(base[i], remove[i]);
      }
    }
    return base;
  }

}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;

import "../openzeppelin/EnumerableMap.sol";
import "../interfaces/IStatController.sol";

library StructLib {
  using EnumerableMap for EnumerableMap.UintToIntMap;

  function flat(EnumerableMap.UintToIntMap storage map) internal view returns (uint[] memory){
    unchecked{
      uint length = map.length();
      uint[] memory values = new uint[](uint(IStatController.ATTRIBUTES.END_SLOT));
      for (uint i; i < length; ++i) {
        (uint index, int value) = map._at(i);
        if (value < 0) {
          value = 0;
        }
        values[index] = uint(value);
      }
      return values;
    }
  }

  function flatInt(EnumerableMap.UintToIntMap storage map) internal view returns (int[] memory){
    unchecked{
      uint length = map.length();
      int[] memory values = new int[](uint(IStatController.ATTRIBUTES.END_SLOT));
      for (uint i; i < length; ++i) {
        (uint index, int value) = map._at(i);
        values[index] = value;
      }
      return values;
    }
  }

  function getOrZeroA(EnumerableMap.UintToIntMap storage map, IStatController.ATTRIBUTES index) internal view returns (uint) {
    (,int value) = map.tryGet(uint(index));
    if (value < 0) {
      value = 0;
    }
    return uint(value);
  }

  function getOrZero(EnumerableMap.UintToIntMap storage map, uint index) internal view returns (uint) {
    (,int value) = map.tryGet(index);
    if (value < 0) {
      value = 0;
    }
    return uint(value);
  }

  function getOrZeroInt(EnumerableMap.UintToIntMap storage map, uint index) internal view returns (int) {
    (,int value) = map.tryGet(index);
    return value;
  }

  function increment(EnumerableMap.UintToIntMap storage map, uint index, uint value) internal returns (uint) {
    (,int oldValue) = map.tryGet(index);
    int newValue = oldValue + int(value);
    map._set(index, newValue);
    return uint(newValue);
  }

  function decrement(EnumerableMap.UintToIntMap storage map, uint index, int value) internal returns (int) {
    (,int oldValue) = map.tryGet(index);
    int newValue = oldValue - int(value);
    if (newValue == 0) {
      map.remove(index);
    } else {
      map._set(index, newValue);
    }

    return newValue;
  }

}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
  /**
   * @dev Returns true if `account` is a contract.
   *
   * [IMPORTANT]
   * ====
   * It is unsafe to assume that an address for which this function returns
   * false is an externally-owned account (EOA) and not a contract.
   *
   * Among others, `isContract` will return false for the following
   * types of addresses:
   *
   *  - an externally-owned account
   *  - a contract in construction
   *  - an address where a contract will be created
   *  - an address where a contract lived, but was destroyed
   * ====
   */
  function isContract(address account) internal view returns (bool) {
    // This method relies on extcodesize, which returns 0 for contracts in
    // construction, since the code is only stored at the end of the
    // constructor execution.

    uint256 size;
    assembly {
      size := extcodesize(account)
    }
    return size > 0;
  }

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

    (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

        assembly {
          let returndata_size := mload(returndata)
          revert(add(32, returndata), returndata_size)
        }
      } else {
        revert(errorMessage);
      }
    }
  }
}

File 25 of 41 : EnumerableMap.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableMap.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableMap.js.

pragma solidity ^0.8.0;

import "./EnumerableSet.sol";

/**
 * @dev Library for managing an enumerable variant of Solidity's
 * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
 * type.
 *
 * Maps have the following properties:
 *
 * - Entries are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Entries are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableMap for EnumerableMap.UintToAddressMap;
 *
 *     // Declare a set state variable
 *     EnumerableMap.UintToAddressMap private myMap;
 * }
 * ```
 *
 * The following map types are supported:
 *
 * - `uint256 -> address` (`UintToAddressMap`) since v3.0.0
 * - `address -> uint256` (`AddressToUintMap`) since v4.6.0
 * - `bytes32 -> bytes32` (`Bytes32ToBytes32Map`) since v4.6.0
 * - `uint256 -> uint256` (`UintToUintMap`) since v4.7.0
 * - `bytes32 -> uint256` (`Bytes32ToUintMap`) since v4.7.0
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableMap, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableMap.
 * ====
 */
library EnumerableMap {
  using EnumerableSet for EnumerableSet.Bytes32Set;

  // To implement this library for multiple types with as little code
  // repetition as possible, we write it in terms of a generic Map type with
  // bytes32 keys and values.
  // The Map implementation uses private functions, and user-facing
  // implementations (such as Uint256ToAddressMap) are just wrappers around
  // the underlying Map.
  // This means that we can only create new EnumerableMaps for types that fit
  // in bytes32.

  struct Bytes32ToBytes32Map {
    // Storage of keys
    EnumerableSet.Bytes32Set _keys;
    mapping(bytes32 => bytes32) _values;
  }

  /**
   * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
  function set(
    Bytes32ToBytes32Map storage map,
    bytes32 key,
    bytes32 value
  ) internal returns (bool) {
    map._values[key] = value;
    return map._keys.add(key);
  }

  /**
   * @dev Removes a key-value pair from a map. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
  function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) {
    delete map._values[key];
    return map._keys.remove(key);
  }

  /**
   * @dev Returns true if the key is in the map. O(1).
     */
  function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) {
    return map._keys.contains(key);
  }

  /**
   * @dev Returns the number of key-value pairs in the map. O(1).
     */
  function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) {
    return map._keys.length();
  }

  /**
   * @dev Returns the key-value pair stored at position `index` in the map. O(1).
     *
     * Note that there are no guarantees on the ordering of entries inside the
     * array, and it may change when more entries are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
  function at(Bytes32ToBytes32Map storage map, uint256 index) internal view returns (bytes32, bytes32) {
    bytes32 key = map._keys.at(index);
    return (key, map._values[key]);
  }

  /**
   * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
  function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool, bytes32) {
    bytes32 value = map._values[key];
    if (value == bytes32(0)) {
      return (contains(map, key), bytes32(0));
    } else {
      return (true, value);
    }
  }

  /**
   * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
  function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) {
    bytes32 value = map._values[key];
    require(value != 0 || contains(map, key), "EnumerableMap: nonexistent key");
    return value;
  }

  /**
   * @dev Same as {_get}, with a custom error message when `key` is not in the map.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {_tryGet}.
     */
  function get(
    Bytes32ToBytes32Map storage map,
    bytes32 key,
    string memory errorMessage
  ) internal view returns (bytes32) {
    bytes32 value = map._values[key];
    require(value != 0 || contains(map, key), errorMessage);
    return value;
  }

  // UintToUintMap

  struct UintToUintMap {
    Bytes32ToBytes32Map _inner;
  }

  /**
   * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
  function set(
    UintToUintMap storage map,
    uint256 key,
    uint256 value
  ) internal returns (bool) {
    return set(map._inner, bytes32(key), bytes32(value));
  }

  /**
   * @dev Removes a value from a set. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
  function remove(UintToUintMap storage map, uint256 key) internal returns (bool) {
    return remove(map._inner, bytes32(key));
  }

  /**
   * @dev Returns true if the key is in the map. O(1).
     */
  function contains(UintToUintMap storage map, uint256 key) internal view returns (bool) {
    return contains(map._inner, bytes32(key));
  }

  /**
   * @dev Returns the number of elements in the map. O(1).
     */
  function length(UintToUintMap storage map) internal view returns (uint256) {
    return length(map._inner);
  }

  /**
   * @dev Returns the element stored at position `index` in the set. O(1).
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
  function at(UintToUintMap storage map, uint256 index) internal view returns (uint256, uint256) {
    (bytes32 key, bytes32 value) = at(map._inner, index);
    return (uint256(key), uint256(value));
  }

  /**
   * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
  function tryGet(UintToUintMap storage map, uint256 key) internal view returns (bool, uint256) {
    (bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
    return (success, uint256(value));
  }

  /**
   * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
  function get(UintToUintMap storage map, uint256 key) internal view returns (uint256) {
    return uint256(get(map._inner, bytes32(key)));
  }

  /**
   * @dev Same as {get}, with a custom error message when `key` is not in the map.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryGet}.
     */
  function get(
    UintToUintMap storage map,
    uint256 key,
    string memory errorMessage
  ) internal view returns (uint256) {
    return uint256(get(map._inner, bytes32(key), errorMessage));
  }

  // UintToAddressMap

  struct UintToAddressMap {
    Bytes32ToBytes32Map _inner;
  }

  /**
   * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
  function set(
    UintToAddressMap storage map,
    uint256 key,
    address value
  ) internal returns (bool) {
    return set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
  }

  /**
   * @dev Removes a value from a set. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
  function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
    return remove(map._inner, bytes32(key));
  }

  /**
   * @dev Returns true if the key is in the map. O(1).
     */
  function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
    return contains(map._inner, bytes32(key));
  }

  /**
   * @dev Returns the number of elements in the map. O(1).
     */
  function length(UintToAddressMap storage map) internal view returns (uint256) {
    return length(map._inner);
  }

  /**
   * @dev Returns the element stored at position `index` in the set. O(1).
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
  function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
    (bytes32 key, bytes32 value) = at(map._inner, index);
    return (uint256(key), address(uint160(uint256(value))));
  }

  /**
   * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
  function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
    (bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
    return (success, address(uint160(uint256(value))));
  }

  /**
   * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
  function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
    return address(uint160(uint256(get(map._inner, bytes32(key)))));
  }

  /**
   * @dev Same as {get}, with a custom error message when `key` is not in the map.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryGet}.
     */
  function get(
    UintToAddressMap storage map,
    uint256 key,
    string memory errorMessage
  ) internal view returns (address) {
    return address(uint160(uint256(get(map._inner, bytes32(key), errorMessage))));
  }

  // AddressToUintMap

  struct AddressToUintMap {
    Bytes32ToBytes32Map _inner;
  }

  /**
   * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
  function set(
    AddressToUintMap storage map,
    address key,
    uint256 value
  ) internal returns (bool) {
    return set(map._inner, bytes32(uint256(uint160(key))), bytes32(value));
  }

  /**
   * @dev Removes a value from a set. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
  function remove(AddressToUintMap storage map, address key) internal returns (bool) {
    return remove(map._inner, bytes32(uint256(uint160(key))));
  }

  /**
   * @dev Returns true if the key is in the map. O(1).
     */
  function contains(AddressToUintMap storage map, address key) internal view returns (bool) {
    return contains(map._inner, bytes32(uint256(uint160(key))));
  }

  /**
   * @dev Returns the number of elements in the map. O(1).
     */
  function length(AddressToUintMap storage map) internal view returns (uint256) {
    return length(map._inner);
  }

  /**
   * @dev Returns the element stored at position `index` in the set. O(1).
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
  function at(AddressToUintMap storage map, uint256 index) internal view returns (address, uint256) {
    (bytes32 key, bytes32 value) = at(map._inner, index);
    return (address(uint160(uint256(key))), uint256(value));
  }

  /**
   * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
  function tryGet(AddressToUintMap storage map, address key) internal view returns (bool, uint256) {
    (bool success, bytes32 value) = tryGet(map._inner, bytes32(uint256(uint160(key))));
    return (success, uint256(value));
  }

  /**
   * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
  function get(AddressToUintMap storage map, address key) internal view returns (uint256) {
    return uint256(get(map._inner, bytes32(uint256(uint160(key)))));
  }

  /**
   * @dev Same as {get}, with a custom error message when `key` is not in the map.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryGet}.
     */
  function get(
    AddressToUintMap storage map,
    address key,
    string memory errorMessage
  ) internal view returns (uint256) {
    return uint256(get(map._inner, bytes32(uint256(uint160(key))), errorMessage));
  }

  // Bytes32ToUintMap

  struct Bytes32ToUintMap {
    Bytes32ToBytes32Map _inner;
  }

  /**
   * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
  function set(
    Bytes32ToUintMap storage map,
    bytes32 key,
    uint256 value
  ) internal returns (bool) {
    return set(map._inner, key, bytes32(value));
  }

  /**
   * @dev Removes a value from a set. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
  function remove(Bytes32ToUintMap storage map, bytes32 key) internal returns (bool) {
    return remove(map._inner, key);
  }

  /**
   * @dev Returns true if the key is in the map. O(1).
     */
  function contains(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool) {
    return contains(map._inner, key);
  }

  /**
   * @dev Returns the number of elements in the map. O(1).
     */
  function length(Bytes32ToUintMap storage map) internal view returns (uint256) {
    return length(map._inner);
  }

  /**
   * @dev Returns the element stored at position `index` in the set. O(1).
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
  function at(Bytes32ToUintMap storage map, uint256 index) internal view returns (bytes32, uint256) {
    (bytes32 key, bytes32 value) = at(map._inner, index);
    return (key, uint256(value));
  }

  /**
   * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
  function tryGet(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool, uint256) {
    (bool success, bytes32 value) = tryGet(map._inner, key);
    return (success, uint256(value));
  }

  /**
   * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
  function get(Bytes32ToUintMap storage map, bytes32 key) internal view returns (uint256) {
    return uint256(get(map._inner, key));
  }

  /**
   * @dev Same as {get}, with a custom error message when `key` is not in the map.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryGet}.
     */
  function get(
    Bytes32ToUintMap storage map,
    bytes32 key,
    string memory errorMessage
  ) internal view returns (uint256) {
    return uint256(get(map._inner, key, errorMessage));
  }

  //////////////////////////////////////////////////
  //          CUSTOM IMPLEMENTATIONS
  //////////////////////////////////////////////////

  // UintToIntMap
  // It is very specific custom structure specific for game mechanics.

  struct UintToIntMap {
    Bytes32ToBytes32Map _inner;
  }

  /// @dev Attention! Use it wisely, with properly checks.
  function _set(
    UintToIntMap storage map,
    uint256 key,
    int256 value
  ) internal returns (bool) {
    return set(map._inner, bytes32(key), bytes32(uint(value)));
  }

  function setUint(
    UintToIntMap storage map,
    uint256 key,
    uint256 value
  ) internal returns (bool) {
    return set(map._inner, bytes32(key), bytes32(value));
  }

  function remove(UintToIntMap storage map, uint256 key) internal returns (bool) {
    return remove(map._inner, bytes32(key));
  }

  function contains(UintToIntMap storage map, uint256 key) internal view returns (bool) {
    return contains(map._inner, bytes32(key));
  }

  function length(UintToIntMap storage map) internal view returns (uint256) {
    return length(map._inner);
  }

  function _at(UintToIntMap storage map, uint256 index) internal view returns (uint256, int256) {
    (bytes32 key, bytes32 value) = at(map._inner, index);
    return (uint256(key), int256(uint(value)));
  }

  function atUint(UintToIntMap storage map, uint256 index) internal view returns (uint256, uint256) {
    (bytes32 key, bytes32 value) = at(map._inner, index);
    int _v = int256(uint(value));
    if (_v < 0) {
      _v = 0;
    }
    return (uint256(key), uint(_v));
  }

  function tryGet(UintToIntMap storage map, uint256 key) internal view returns (bool, int256) {
    (bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
    return (success, int256(uint(value)));
  }

  function _get(UintToIntMap storage map, uint256 key) internal view returns (int256) {
    return int256(uint(get(map._inner, bytes32(key))));
  }

  function getUint(UintToIntMap storage map, uint256 key) internal view returns (uint256) {
    int value = int256(uint(get(map._inner, bytes32(key))));
    if (value < 0) {
      value = 0;
    }
    return uint(value);
  }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
  // To implement this library for multiple types with as little code
  // repetition as possible, we write it in terms of a generic Set type with
  // bytes32 values.
  // The Set implementation uses private functions, and user-facing
  // implementations (such as AddressSet) are just wrappers around the
  // underlying Set.
  // This means that we can only create new EnumerableSets for types that fit
  // in bytes32.

  struct Set {
    // Storage of set values
    bytes32[] _values;
    // Position of the value in the `values` array, plus 1 because index 0
    // means a value is not in the set.
    mapping(bytes32 => uint256) _indexes;
  }

  /**
   * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
  function _add(Set storage set, bytes32 value) private returns (bool) {
    if (!_contains(set, value)) {
      set._values.push(value);
      // The value is stored at length-1, but we add 1 to all indexes
      // and use 0 as a sentinel value
      set._indexes[value] = set._values.length;
      return true;
    } else {
      return false;
    }
  }

  /**
   * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
  function _remove(Set storage set, bytes32 value) private returns (bool) {
    // We read and store the value's index to prevent multiple reads from the same storage slot
    uint256 valueIndex = set._indexes[value];

    if (valueIndex != 0) {
      // Equivalent to contains(set, value)
      // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
      // the array, and then remove the last element (sometimes called as 'swap and pop').
      // This modifies the order of the array, as noted in {at}.

      uint256 toDeleteIndex = valueIndex - 1;
      uint256 lastIndex = set._values.length - 1;

      if (lastIndex != toDeleteIndex) {
        bytes32 lastValue = set._values[lastIndex];

        // Move the last value to the index where the value to delete is
        set._values[toDeleteIndex] = lastValue;
        // Update the index for the moved value
        set._indexes[lastValue] = valueIndex;
        // Replace lastValue's index to valueIndex
      }

      // Delete the slot where the moved value was stored
      set._values.pop();

      // Delete the index for the deleted slot
      delete set._indexes[value];

      return true;
    } else {
      return false;
    }
  }

  /**
   * @dev Returns true if the value is in the set. O(1).
     */
  function _contains(Set storage set, bytes32 value) private view returns (bool) {
    return set._indexes[value] != 0;
  }

  /**
   * @dev Returns the number of values on the set. O(1).
     */
  function _length(Set storage set) private view returns (uint256) {
    return set._values.length;
  }

  /**
   * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
  function _at(Set storage set, uint256 index) private view returns (bytes32) {
    return set._values[index];
  }

  /**
   * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
  function _values(Set storage set) private view returns (bytes32[] memory) {
    return set._values;
  }

  // Bytes32Set

  struct Bytes32Set {
    Set _inner;
  }

  /**
   * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
  function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
    return _add(set._inner, value);
  }

  /**
   * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
  function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
    return _remove(set._inner, value);
  }

  /**
   * @dev Returns true if the value is in the set. O(1).
     */
  function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
    return _contains(set._inner, value);
  }

  /**
   * @dev Returns the number of values in the set. O(1).
     */
  function length(Bytes32Set storage set) internal view returns (uint256) {
    return _length(set._inner);
  }

  /**
   * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
  function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
    return _at(set._inner, index);
  }

  /**
   * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
  function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
    bytes32[] memory store = _values(set._inner);
    bytes32[] memory result;

    /// @solidity memory-safe-assembly
    assembly {
      result := store
    }

    return result;
  }

  // AddressSet

  struct AddressSet {
    Set _inner;
  }

  /**
   * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
  function add(AddressSet storage set, address value) internal returns (bool) {
    return _add(set._inner, bytes32(uint256(uint160(value))));
  }

  /**
   * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
  function remove(AddressSet storage set, address value) internal returns (bool) {
    return _remove(set._inner, bytes32(uint256(uint160(value))));
  }

  /**
   * @dev Returns true if the value is in the set. O(1).
     */
  function contains(AddressSet storage set, address value) internal view returns (bool) {
    return _contains(set._inner, bytes32(uint256(uint160(value))));
  }

  /**
   * @dev Returns the number of values in the set. O(1).
     */
  function length(AddressSet storage set) internal view returns (uint256) {
    return _length(set._inner);
  }

  /**
   * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
  function at(AddressSet storage set, uint256 index) internal view returns (address) {
    return address(uint160(uint256(_at(set._inner, index))));
  }

  /**
   * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
  function values(AddressSet storage set) internal view returns (address[] memory) {
    bytes32[] memory store = _values(set._inner);
    address[] memory result;

    /// @solidity memory-safe-assembly
    assembly {
      result := store
    }

    return result;
  }

  // UintSet

  struct UintSet {
    Set _inner;
  }

  /**
   * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
  function add(UintSet storage set, uint256 value) internal returns (bool) {
    return _add(set._inner, bytes32(value));
  }

  /**
   * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
  function remove(UintSet storage set, uint256 value) internal returns (bool) {
    return _remove(set._inner, bytes32(value));
  }

  /**
   * @dev Returns true if the value is in the set. O(1).
     */
  function contains(UintSet storage set, uint256 value) internal view returns (bool) {
    return _contains(set._inner, bytes32(value));
  }

  /**
   * @dev Returns the number of values in the set. O(1).
     */
  function length(UintSet storage set) internal view returns (uint256) {
    return _length(set._inner);
  }

  /**
   * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
  function at(UintSet storage set, uint256 index) internal view returns (uint256) {
    return uint256(_at(set._inner, index));
  }

  /**
   * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
  function values(UintSet storage set) internal view returns (uint256[] memory) {
    bytes32[] memory store = _values(set._inner);
    uint256[] memory result;

    /// @solidity memory-safe-assembly
    assembly {
      result := store
    }

    return result;
  }
}

// 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;
  }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/ERC721Enumerable.sol)

pragma solidity ^0.8.0;

import "./ERC721Upgradeable.sol";
import "./IERC721Enumerable.sol";
import "./Initializable.sol";

/**
 * @dev This implements an optional extension of {ERC721} defined in the EIP that adds
 * enumerability of all the token ids in the contract as well as all token ids owned by each
 * account.
 */
abstract contract ERC721EnumerableUpgradeable is Initializable, ERC721Upgradeable, IERC721Enumerable {
  function __ERC721Enumerable_init() internal view {
    _onlyInitializing();
  }

  function __ERC721Enumerable_init_unchained() internal view {
    _onlyInitializing();
  }
  // Mapping from owner to list of owned token IDs
  mapping(address => mapping(uint256 => uint256)) private _ownedTokens;

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

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

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

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

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

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

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

  /**
   * @dev Hook that is called before any token transfer. This includes minting
   * and burning.
   *
   * 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, ``from``'s `tokenId` will be burned.
   * - `from` cannot be the zero address.
   * - `to` cannot be the zero address.
   *
   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
   */
  function _beforeTokenTransfer(
    address from,
    address to,
    uint256 tokenId
  ) internal virtual override {
    super._beforeTokenTransfer(from, to, tokenId);

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

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

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

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

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

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

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

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

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

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

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

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

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

  /**
   * @dev This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   */
  uint256[46] private __gap;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.0;

import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "./IERC721Metadata.sol";
import "./Address.sol";
import "./Strings.sol";
import "./ERC165.sol";
import "./Initializable.sol";
import "../relay/ERC2771Context.sol";

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

  // Token name
  string private _name;

  // Token symbol
  string private _symbol;

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

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

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

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

  /**
   * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
   */
  function __ERC721_init(string memory name_, string memory symbol_) internal {
    _onlyInitializing();
    __ERC721_init_unchained(name_, symbol_);
  }

  function __ERC721_init_unchained(string memory name_, string memory symbol_) internal {
    _onlyInitializing();
    _name = name_;
    _symbol = symbol_;
  }

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

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

  /**
   * @dev See {IERC721-ownerOf}.
   */
  function ownerOf(uint256 tokenId) public view virtual override returns (address) {
    address owner = _owners[tokenId];
//    require(owner != address(0), "ERC721: owner query for nonexistent token");
    return owner;
  }

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

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

  /**
   * @dev See {IERC721Metadata-tokenURI}.
   */
  function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
//    require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

    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 virtual override {
    address owner = ERC721Upgradeable.ownerOf(tokenId);
    require(to != owner, "ERC721: approval to current owner");

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

    _approve(to, tokenId);
  }

  /**
   * @dev See {IERC721-getApproved}.
   */
  function getApproved(uint256 tokenId) public view virtual override returns (address) {
    require(_exists(tokenId), "ERC721: approved query for nonexistent token");

    return _tokenApprovals[tokenId];
  }

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

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

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

    _transfer(from, to, tokenId);
  }

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

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

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

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

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

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

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

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

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

    _balances[to] += 1;
    _owners[tokenId] = to;

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

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

  /**
   * @dev Destroys `tokenId`.
   * The approval is cleared when the token is burned.
   *
   * Requirements:
   *
   * - `tokenId` must exist.
   *
   * Emits a {Transfer} event.
   */
  function _burn(uint256 tokenId) internal virtual {
    address owner = ERC721Upgradeable.ownerOf(tokenId);

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

    // Clear approvals
    _approve(address(0), tokenId);

    _balances[owner] -= 1;
    delete _owners[tokenId];

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

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

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

    _beforeTokenTransfer(from, to, tokenId);

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

    _balances[from] -= 1;
    _balances[to] += 1;
    _owners[tokenId] = to;

    emit Transfer(from, to, tokenId);

    _afterTokenTransfer(from, to, tokenId);
  }

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

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

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

  /**
   * @dev Hook that is called before any token transfer. This includes minting
   * and burning.
   *
   * 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, ``from``'s `tokenId` will be burned.
   * - `from` and `to` are never both zero.
   *
   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
   */
  function _beforeTokenTransfer(
    address from,
    address to,
    uint256 tokenId
  ) internal virtual {}

  /**
   * @dev Hook that is called after any transfer of tokens. This includes
   * minting and burning.
   *
   * Calling conditions:
   *
   * - when `from` and `to` are both non-zero.
   * - `from` and `to` are never both zero.
   *
   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
   */
  function _afterTokenTransfer(
    address from,
    address to,
    uint256 tokenId
  ) internal virtual {}

  /**
   * @dev This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   */
  uint256[44] private __gap;
}

// SPDX-License-Identifier: MIT

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);
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
  /**
   * @dev Returns the amount of tokens in existence.
   */
  function totalSupply() external view returns (uint256);

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

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

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

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

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

  /**
   * @dev Emitted when `value` tokens are moved from one account (`from`) to
   * another (`to`).
   *
   * Note that `value` may be zero.
   */
  event Transfer(address indexed from, address indexed to, uint256 value);

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

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./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`, 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 be 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 Returns the account approved for `tokenId` token.
   *
   * Requirements:
   *
   * - `tokenId` must exist.
   */
  function getApproved(uint256 tokenId) external view returns (address operator);

  /**
   * @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 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);

  /**
   * @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;
}

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

pragma solidity ^0.8.0;

import "./IERC721.sol";

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

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

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

// 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 35 of 41 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT

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 `IERC721.onERC721Received.selector`.
   */
  function onERC721Received(
    address operator,
    address from,
    uint256 tokenId,
    bytes calldata data
  ) external returns (bytes4);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.0;

import "./Address.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the
 * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() initializer {}
 * ```
 * ====
 */
abstract contract Initializable {
  /**
   * @dev Indicates that the contract has been initialized.
   */
  bool private _initialized;

  /**
   * @dev Indicates that the contract is in the process of being initialized.
   */
  bool private _initializing;

  /**
   * @dev Modifier to protect an initializer function from being invoked twice.
   */
  modifier initializer() {
    // If the contract is initializing we ignore whether _initialized is set in order to support multiple
    // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
    // contract may have been reentered.
    require(_initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized");

    bool isTopLevelCall = !_initializing;
    if (isTopLevelCall) {
      _initializing = true;
      _initialized = true;
    }

    _;

    if (isTopLevelCall) {
      _initializing = false;
    }
  }

  /**
   * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
   * {initializer} modifier, directly or indirectly.
   */
  modifier onlyInitializing() {
    require(_initializing, "Initializable: contract is not initializing");
    _;
  }

  function _isConstructor() private view returns (bool) {
    return !Address.isContract(address(this));
  }

  // ----------------  Additional functions for reduce contract size

  /**
   * @dev Modifier to protect an initializer function from being invoked twice.
   */
  function _initializer() internal {
    // If the contract is initializing we ignore whether _initialized is set in order to support multiple
    // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
    // contract may have been reentered.
    require(_initializing ? _isConstructor() : !_initialized, "initialized");

    if (!_initializing) {
      _initializing = true;
      _initialized = true;
    }
  }

  function _finishInitializer() internal {
    if (!_initializing) {
      _initializing = false;
    }
  }

  /**
   * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
   * {initializer} modifier, directly or indirectly.
   */
  function _onlyInitializing() internal view {
    require(_initializing, "initializing");
  }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
  /**
   * @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 / b + (a % b == 0 ? 0 : 1);
  }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
  using Address for address;

  function safeTransfer(
    IERC20 token,
    address to,
    uint256 value
  ) internal {
    _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
  }

  function safeTransferFrom(
    IERC20 token,
    address from,
    address to,
    uint256 value
  ) internal {
    _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
  }

  /**
   * @dev Deprecated. This function has issues similar to the ones found in
   * {IERC20-approve}, and its usage is discouraged.
   *
   * Whenever possible, use {safeIncreaseAllowance} and
   * {safeDecreaseAllowance} instead.
   */
  function safeApprove(
    IERC20 token,
    address spender,
    uint256 value
  ) internal {
    // safeApprove should only be called when setting an initial allowance,
    // or when resetting it to zero. To increase and decrease it, use
    // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
    require(
      (value == 0) || (token.allowance(address(this), spender) == 0),
      "SafeERC20: approve from non-zero to non-zero allowance"
    );
    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
  }

  function safeIncreaseAllowance(
    IERC20 token,
    address spender,
    uint256 value
  ) internal {
    uint256 newAllowance = token.allowance(address(this), spender) + value;
    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
  }

  function safeDecreaseAllowance(
    IERC20 token,
    address spender,
    uint256 value
  ) internal {
    unchecked {
      uint256 oldAllowance = token.allowance(address(this), spender);
      require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
      uint256 newAllowance = oldAllowance - value;
      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }
  }

  /**
   * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
   * on the return value: the return value is optional (but if data is returned, it must not be false).
   * @param token The token targeted by the call.
   * @param data The call data (encoded using abi.encode or one of its variants).
   */
  function _callOptionalReturn(IERC20 token, bytes memory data) private {
    // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
    // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
    // the target address contains contract code and also asserts for success in the low-level call.

    bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
    if (returndata.length > 0) {
      // Return data is optional
      require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }
  }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)

pragma solidity ^0.8.0;

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

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

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

  /**
   * @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);
  }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;

import "../openzeppelin/Initializable.sol";
import "../interfaces/IControllable.sol";
import "../interfaces/IController.sol";
import "../lib/SlotsLib.sol";

/// @title Implement basic functionality for any contract that require strict control
/// @dev Can be used with upgradeable pattern.
///      Require call __Controllable_init() in any case.
/// @author belbix
abstract contract Controllable is Initializable, IControllable {
  using SlotsLib for bytes32;

  /// @notice Version of the contract
  /// @dev Should be incremented when contract changed
  string public constant CONTROLLABLE_VERSION = "1.0.0";

  bytes32 internal constant _CONTROLLER_SLOT = bytes32(uint256(keccak256("eip1967.controllable.controller")) - 1);
  bytes32 internal constant _CREATED_SLOT = bytes32(uint256(keccak256("eip1967.controllable.created")) - 1);
  bytes32 internal constant _CREATED_BLOCK_SLOT = bytes32(uint256(keccak256("eip1967.controllable.created_block")) - 1);
  bytes32 internal constant _REVISION_SLOT = bytes32(uint256(keccak256("eip1967.controllable.revision")) - 1);
  bytes32 internal constant _PREVIOUS_LOGIC_SLOT = bytes32(uint256(keccak256("eip1967.controllable.prev_logic")) - 1);

  event ContractInitialized(address controller, uint ts, uint block);
  event RevisionIncreased(uint value, address oldLogic);

  // init implementation contract
  constructor() initializer {}

  /// @notice Initialize contract after setup it as proxy implementation
  ///         Save block.timestamp in the "created" variable
  /// @dev Use it only once after first logic setup
  /// @param controller_ Controller address
  function __Controllable_init(address controller_) public {
    _onlyInitializing();
    require(controller_ != address(0), "Zero controller");
    _CONTROLLER_SLOT.set(controller_);
    _CREATED_SLOT.set(block.timestamp);
    _CREATED_BLOCK_SLOT.set(block.number);
    emit ContractInitialized(controller_, block.timestamp, block.number);
  }

  /// @dev Return true if given address is controller
  function isController(address _value) public override view returns (bool) {
    return _value == controller();
  }

  /// @notice Return true if given address is setup as governance in Controller
  function isGovernance(address _value) public override view returns (bool) {
    return IController(controller()).governance() == _value;
  }

  /// @dev Contract upgrade counter
  function revision() external view override returns (uint) {
    return _REVISION_SLOT.getUint();
  }

  /// @dev Previous logic implementation
  function previousImplementation() external view override returns (address) {
    return _PREVIOUS_LOGIC_SLOT.getAddress();
  }

  // ************* SETTERS/GETTERS *******************

  /// @notice Return controller address saved in the contract slot
  function controller() public view override returns (address) {
    return _CONTROLLER_SLOT.getAddress();
  }

  /// @notice Return creation timestamp
  /// @return Creation timestamp
  function created() external view override returns (uint256) {
    return _CREATED_SLOT.getUint();
  }

  /// @notice Return creation block number
  /// @return Creation block number
  function createdBlock() external override view returns (uint256) {
    return _CREATED_BLOCK_SLOT.getUint();
  }

  /// @dev Revision should be increased on each contract upgrade
  function increaseRevision(address oldLogic) external override {
    require(msg.sender == address(this), "Increase revision forbidden");
    uint r = _REVISION_SLOT.getUint() + 1;
    _REVISION_SLOT.set(r);
    _PREVIOUS_LOGIC_SLOT.set(oldLogic);
    emit RevisionIncreased(r, oldLogic);
  }

}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (metatx/ERC2771Context.sol)

pragma solidity ^0.8.1;

/**
 * @dev Context variant with ERC2771 support.
 */
// based on https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/metatx/ERC2771Context.sol
abstract contract ERC2771Context {

  // for whitelist new relayers need to add new constants and update proxies
//  address private constant GELATO_RELAY = 0xaBcC9b596420A9E9172FD5938620E265a0f9Df92;
//  address private constant GELATO_RELAY_ERC_2771 = 0xb539068872230f20456CF38EC52EF2f91AF4AE49;
//  address private constant GELATO_RELAY_CONCURRENT_ERC_2771 = 0x8598806401A63Ddf52473F1B3C55bC9E33e2d73b;
//  address private constant GELATO_RELAY_1_BALANCE = 0x75bA5Af8EFFDCFca32E1e288806d54277D1fde99;
  address private constant GELATO_RELAY_1_BALANCE_ERC_2771 = 0xd8253782c45a12053594b9deB72d8e8aB2Fca54c;
//  address private constant GELATO_RELAY_1_BALANCE_CONCURRENT_ERC_2771 = 0xc65d82ECE367EF06bf2AB791B3f3CF037Dc0e816;

  function isTrustedForwarder(address forwarder) public view virtual returns (bool){
    return forwarder == GELATO_RELAY_1_BALANCE_ERC_2771;
  }

  function _msgSender() internal view virtual returns (address sender) {
    if (isTrustedForwarder(msg.sender)) {
      // The assembly code is more direct than the Solidity version using `abi.decode`.
      /// @solidity memory-safe-assembly
      assembly {
        sender := shr(96, calldataload(sub(calldatasize(), 20)))
      }
      return sender;
    } else {
      return msg.sender;
    }
  }

  function _msgData() internal view virtual returns (bytes calldata) {
    if (isTrustedForwarder(msg.sender)) {
      return msg.data[: msg.data.length - 20];
    } else {
      return msg.data;
    }
  }

  /// @notice Return true if given address is not a smart contract but a wallet address.
  /// @dev It is not 100% guarantee after EIP-3074 implementation, use it as an additional check.
  /// @return true if the address is a wallet.
  function _isNotSmartContract() internal view returns (bool) {
    return isTrustedForwarder(msg.sender) || msg.sender == tx.origin;
  }
}

Settings
{
  "evmVersion": "istanbul",
  "optimizer": {
    "enabled": true,
    "runs": 150
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {
    "contracts/lib/ItemLib.sol": {
      "ItemLib": "0x00379dd90b2a337c4652e286e4fbceadef940a21"
    }
  }
}

Contract ABI

API
[{"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":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"consumedId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"augmentationLevel","type":"uint256"}],"name":"Augmented","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"BaseDurabilityChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"uri","type":"string"}],"name":"BaseUriChanged","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"},{"internalType":"bool[]","name":"increase","type":"bool[]"}],"indexed":false,"internalType":"struct IConsumableItem.GenerateConsumableInfo","name":"info","type":"tuple"},{"components":[{"internalType":"uint256","name":"level","type":"uint256"},{"internalType":"uint256","name":"experience","type":"uint256"},{"internalType":"uint256","name":"life","type":"uint256"},{"internalType":"uint256","name":"mana","type":"uint256"},{"internalType":"uint256","name":"lifeChances","type":"uint256"}],"indexed":false,"internalType":"struct IStatController.ChangeableStats","name":"stats","type":"tuple"}],"name":"ConsumableInfoChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"controller","type":"address"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"block","type":"uint256"}],"name":"ContractInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Destroyed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"heroToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"heroTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"itemSlot","type":"uint256"}],"name":"Equipped","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"mins","type":"uint256[]"},{"internalType":"uint256[]","name":"maxs","type":"uint256[]"},{"internalType":"uint256[]","name":"chances","type":"uint256[]"},{"internalType":"uint256","name":"minRandomAttributes","type":"uint256"},{"internalType":"uint256","name":"maxRandomAttributes","type":"uint256"}],"indexed":false,"internalType":"struct IItem.GenerateInfo","name":"info","type":"tuple"},{"components":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"mins","type":"uint256[]"},{"internalType":"uint256[]","name":"maxs","type":"uint256[]"},{"internalType":"uint256[]","name":"chances","type":"uint256[]"},{"internalType":"uint256","name":"minRandomAttributes","type":"uint256"},{"internalType":"uint256","name":"maxRandomAttributes","type":"uint256"}],"indexed":false,"internalType":"struct IItem.GenerateInfo","name":"negativeInfo","type":"tuple"}],"name":"GenInfoChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"consumedItemId","type":"uint256"}],"name":"ItemRepaired","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rarity","type":"uint256"}],"name":"Minted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"consumedId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"augmentationLevel","type":"uint256"}],"name":"NotAugmented","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newDurability","type":"uint256"}],"name":"ReduceDurability","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256","name":"strength","type":"uint256"},{"internalType":"uint256","name":"dexterity","type":"uint256"},{"internalType":"uint256","name":"vitality","type":"uint256"},{"internalType":"uint256","name":"energy","type":"uint256"}],"indexed":false,"internalType":"struct IStatController.CoreAttributes","name":"requirements","type":"tuple"}],"name":"RequirementsChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"address","name":"oldLogic","type":"address"}],"name":"RevisionIncreased","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"heroToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"heroTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"itemSlot","type":"uint256"},{"indexed":false,"internalType":"address","name":"destination","type":"address"}],"name":"TakenOff","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"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"string","name":"uri","type":"string"}],"name":"UniqueUriChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"uri","type":"string"},{"indexed":false,"internalType":"uint256","name":"rarity","type":"uint256"}],"name":"UriByRarityChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"address","name":"hero","type":"address"},{"indexed":false,"internalType":"uint256","name":"heroId","type":"uint256"}],"name":"Used","type":"event"},{"inputs":[],"name":"CONSUMABLE_ITEM_BASE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CONTROLLABLE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ITEM_BASE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NFT_BASE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"controller_","type":"address"}],"name":"__Controllable_init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"consumedItemId","type":"uint256"}],"name":"augment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"augmentToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"augmentTokenAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"augmentationLevel","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":[],"name":"baseDurability","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"consumableAttributesAndStats","outputs":[{"internalType":"uint256[]","name":"positive","type":"uint256[]"},{"internalType":"uint256[]","name":"negative","type":"uint256[]"},{"components":[{"internalType":"uint256","name":"level","type":"uint256"},{"internalType":"uint256","name":"experience","type":"uint256"},{"internalType":"uint256","name":"life","type":"uint256"},{"internalType":"uint256","name":"mana","type":"uint256"},{"internalType":"uint256","name":"lifeChances","type":"uint256"}],"internalType":"struct IStatController.ChangeableStats","name":"stats","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"controller","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"created","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"createdBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"destroy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"heroToken","type":"address"},{"internalType":"uint256","name":"heroTokenId","type":"uint256"},{"internalType":"uint256","name":"itemSlot","type":"uint256"}],"name":"equip","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"equipped","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"equippedOn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"exists","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":"oldLogic","type":"address"}],"name":"increaseRevision","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"controller_","type":"address"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"string","name":"uri_","type":"string"},{"internalType":"address","name":"augmentToken_","type":"address"},{"internalType":"uint256","name":"augmentTokenAmount_","type":"uint256"},{"internalType":"uint256","name":"itemLevel_","type":"uint256"},{"internalType":"enum IItem.ItemType","name":"itemType_","type":"uint8"},{"internalType":"uint256","name":"baseDurability_","type":"uint256"}],"name":"initItem","outputs":[],"stateMutability":"nonpayable","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":"isAttackItem","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"isBuffItem","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"isConsumableItem","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_value","type":"address"}],"name":"isController","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_value","type":"address"}],"name":"isGovernance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isItem","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"forwarder","type":"address"}],"name":"isTrustedForwarder","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"itemAttributes","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"itemDurability","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"itemLevel","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"itemMetaType","outputs":[{"internalType":"enum IItem.ItemMetaType","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"itemRarity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"itemType","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mint","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"negativeItemAttributes","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"previousImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"dungeonBiomeLevel","type":"uint256"}],"name":"reduceDurability","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"consumedItemId","type":"uint256"}],"name":"repairDurability","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requirementAttributes","outputs":[{"components":[{"internalType":"uint256","name":"strength","type":"uint256"},{"internalType":"uint256","name":"dexterity","type":"uint256"},{"internalType":"uint256","name":"vitality","type":"uint256"},{"internalType":"uint256","name":"energy","type":"uint256"}],"internalType":"struct IStatController.CoreAttributes","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"revision","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":"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":"uint256","name":"tokenId","type":"uint256"}],"name":"score","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setBaseDurability","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"value","type":"string"}],"name":"setBaseUri","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"},{"internalType":"bool[]","name":"increase","type":"bool[]"}],"internalType":"struct IConsumableItem.GenerateConsumableInfo","name":"consumableGenerateInfo_","type":"tuple"},{"components":[{"internalType":"uint256","name":"level","type":"uint256"},{"internalType":"uint256","name":"experience","type":"uint256"},{"internalType":"uint256","name":"life","type":"uint256"},{"internalType":"uint256","name":"mana","type":"uint256"},{"internalType":"uint256","name":"lifeChances","type":"uint256"}],"internalType":"struct IStatController.ChangeableStats","name":"consumableStatsInfo_","type":"tuple"}],"name":"setConsumableInfo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"mins","type":"uint256[]"},{"internalType":"uint256[]","name":"maxs","type":"uint256[]"},{"internalType":"uint256[]","name":"chances","type":"uint256[]"},{"internalType":"uint256","name":"minRandomAttributes","type":"uint256"},{"internalType":"uint256","name":"maxRandomAttributes","type":"uint256"}],"internalType":"struct IItem.GenerateInfo","name":"_info","type":"tuple"},{"components":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"mins","type":"uint256[]"},{"internalType":"uint256[]","name":"maxs","type":"uint256[]"},{"internalType":"uint256[]","name":"chances","type":"uint256[]"},{"internalType":"uint256","name":"minRandomAttributes","type":"uint256"},{"internalType":"uint256","name":"maxRandomAttributes","type":"uint256"}],"internalType":"struct IItem.GenerateInfo","name":"_negativeInfo","type":"tuple"},{"internalType":"uint256","name":"strengthReq","type":"uint256"},{"internalType":"uint256","name":"dexterityReq","type":"uint256"},{"internalType":"uint256","name":"vitalityReq","type":"uint256"},{"internalType":"uint256","name":"energyReq","type":"uint256"},{"internalType":"uint256","name":"defaultRarity_","type":"uint256"}],"name":"setItemMeta","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"uri","type":"string"},{"internalType":"uint256","name":"rarity","type":"uint256"}],"name":"setItemUriByRarity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"string","name":"uri","type":"string"}],"name":"setUniqueUri","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"},{"internalType":"address","name":"heroToken","type":"address"},{"internalType":"uint256","name":"heroTokenId","type":"uint256"},{"internalType":"uint256","name":"itemSlot","type":"uint256"},{"internalType":"address","name":"destination","type":"address"},{"internalType":"bool","name":"broken","type":"bool"}],"name":"takeOff","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"heroToken","type":"address"},{"internalType":"uint256","name":"heroTokenId","type":"uint256"}],"name":"use","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405260016065553480156200001657600080fd5b50600054610100900460ff16620000345760005460ff161562000038565b303b155b620000a05760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840160405180910390fd5b600054610100900460ff16158015620000c3576000805461ffff19166101011790555b8015620000d6576000805461ff00191690555b5060978054600160ff19918216811790925560d980549091169091179055615ef680620001046000396000f3fe608060405234801561001057600080fd5b50600436106104075760003560e01c80636c0360eb11610220578063b423799e11610130578063d909ea5f116100b8578063f020c59411610087578063f020c594146108b3578063f5470d7b146108f3578063f77c4791146108fc578063fcc78fa514610904578063ffa1ad741461072157600080fd5b8063d909ea5f146108b3578063dee1f0e4146108ba578063e327a6af146108cd578063e985e9c5146108e057600080fd5b8063b9114ce1116100ff578063b9114ce114610854578063bc0d396b14610867578063c87b56dd1461087a578063d1089f331461088d578063d1730f1f146108a057600080fd5b8063b423799e14610805578063b429afeb14610825578063b75f4d8f14610838578063b88d4fde1461084157600080fd5b80639845f17a116101b3578063a0bcfc7f11610182578063a0bcfc7f1461078e578063a0fe0f51146107a1578063a22cb465146107b4578063a5d3bafb146107c7578063a60138a7146107f257600080fd5b80639845f17a1461074d5780639d0bcca0146107605780639d118770146107685780639e2cfb0f1461077b57600080fd5b8063820e5865116101ef578063820e5865146106ec5780638e776f9c1461070e578063936725ec1461072157806395d89b411461074557600080fd5b80636c0360eb146106a957806370a08231146106b1578063718df8fa146106c45780637cc96380146106e457600080fd5b806336c944851161031b5780634f558e79116102ae578063583eab451161027d578063583eab451461063b5780636352211e1461065b57806364f2d7531461066e57806366679a0614610683578063681641381461069657600080fd5b80634f558e79146105ef5780634f6ccce7146106025780634fac6ccd14610615578063572b6c051461062857600080fd5b80633e3008b6116102ea5780633e3008b6146105c157806342842e0e146105d45780634593144c146105e75780634dd8481d1461055e57600080fd5b806336c94485146105895780633710c70a1461059c5780633c97cf60146105af5780633d54e23b146105b857600080fd5b80631f2a5ace1161039e5780632f745c591161036d5780632f745c5914610543578063325a19f11461055657806332d671a11461055e57806335320ba21461058257806335798ed01461058257600080fd5b80631f2a5ace146104ea578063203e597c1461050e57806323b872dd146105215780632e2da5171461053457600080fd5b80631249c58b116103da5780631249c58b1461048957806318160ddd1461049f5780631a8abe9a146104a75780631c001d77146104ca57600080fd5b806301ffc9a71461040c57806306fdde0314610434578063081812fc14610449578063095ea7b314610474575b600080fd5b61041f61041a366004614374565b61091c565b60405190151581526020015b60405180910390f35b61043c610947565b60405161042b91906143d7565b61045c6104573660046143ea565b6109d9565b6040516001600160a01b03909116815260200161042b565b610487610482366004614428565b610a66565b005b610491610b88565b60405190815260200161042b565b603554610491565b61041f6104b53660046143ea565b609f6020526000908152604090205460ff1681565b6104dd6104d83660046143ea565b610b9f565b60405161042b9190614490565b61043c60405180604001604052806005815260200164312e302e3360d81b81525081565b61048761051c3660046144a3565b610bb9565b61048761052f3660046144c5565b610c33565b600460405161042b919061451c565b610491610551366004614428565b610c6b565b610491610d01565b61043c60405180604001604052806005815260200164312e302e3160d81b81525081565b600161041f565b610487610597366004614724565b610d35565b6104dd6105aa3660046143ea565b610f5a565b61049160985481565b61049160995481565b6104876105cf3660046147b3565b610f74565b6104876105e23660046144c5565b6112b7565b6104916112d2565b61041f6105fd3660046143ea565b611302565b6104916106103660046143ea565b61130d565b6104876106233660046147f0565b6113a0565b61041f6106363660046147f0565b6114d3565b6104916106493660046143ea565b609e6020526000908152604090205481565b61045c6106693660046143ea565b6114f5565b610676611510565b60405161042b919061480d565b6104876106913660046148be565b611568565b6104876106a4366004614995565b6115d3565b61043c611625565b6104916106bf3660046147f0565b61162f565b6104916106d23660046143ea565b60a26020526000908152604090205481565b6104916116b6565b6106ff6106fa3660046143ea565b6116e6565b60405161042b939291906149d9565b61048761071c366004614a47565b61179f565b61043c604051806040016040528060058152602001640312e302e360dc1b81525081565b61043c611b74565b61048761075b366004614aae565b611b83565b61045c611ff7565b6104876107763660046143ea565b612027565b6104876107893660046143ea565b6121ef565b61048761079c366004614ad5565b612233565b6104876107af3660046147f0565b612277565b6104876107c2366004614b09565b61239c565b6104916107d5366004614b42565b60b460209081526000928352604080842090915290825290205481565b610487610800366004614b67565b6123ae565b6104916108133660046143ea565b60a06020526000908152604090205481565b61041f6108333660046147f0565b612400565b610491609b5481565b61048761084f366004614bad565b612425565b610487610862366004614c9b565b61245e565b6104916108753660046144a3565b61251b565b61043c6108883660046143ea565b612683565b61048761089b3660046144a3565b612794565b6104916108ae3660046143ea565b6129ce565b600061041f565b61041f6108c83660046147f0565b612ac3565b61041f6108db366004614428565b612b48565b61041f6108ee366004614dc4565b612b6e565b610491609a5481565b61045c612b9c565b60975461045c9061010090046001600160a01b031681565b60006001600160e01b0319821663780e9d6360e01b1480610941575061094182612bcc565b92915050565b60606001805461095690614df2565b80601f016020809104026020016040519081016040528092919081815260200182805461098290614df2565b80156109cf5780601f106109a4576101008083540402835291602001916109cf565b820191906000526020600020905b8154815290600101906020018083116109b257829003601f168201915b5050505050905090565b60006109e482612c1c565b610a4a5760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b60648201526084015b60405180910390fd5b506000908152600560205260409020546001600160a01b031690565b6000610a71826114f5565b9050806001600160a01b0316836001600160a01b031603610ade5760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608401610a41565b806001600160a01b0316610af0612c39565b6001600160a01b03161480610b0c5750610b0c816108ee612c39565b610b795760405162461bcd60e51b815260206004820152603860248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f776044820152771b995c881b9bdc88185c1c1c9bdd995908199bdc88185b1b60421b6064820152608401610a41565b610b838383612c5b565b505050565b6000610b92612cc9565b610b9a612d74565b905090565b6000818152609c6020526040902060609061094190612f87565b610bc1613016565b610bdd5760405162461bcd60e51b8152600401610a4190614e2c565b610be68261302e565b610bef8161302e565b60405162461bcd60e51b815260206004820152600a602482015269636f6e73756d61626c6560b01b6044820152606401610a41565b60405180910390a15b50505050565b610c44610c3e612c39565b826130aa565b610c605760405162461bcd60e51b8152600401610a4190614e49565b610b8383838361316c565b6000610c768361162f565b8210610cd85760405162461bcd60e51b815260206004820152602b60248201527f455243373231456e756d657261626c653a206f776e657220696e646578206f7560448201526a74206f6620626f756e647360a81b6064820152608401610a41565b506001600160a01b03919091166000908152603360209081526040808320938352929052205490565b6000610b9a610d3160017f6f55f470bdc9cb5f04223fd822021061668e4dccb43e8727b295106dc9769c8b614eb0565b5490565b610d3d613313565b6002876080015110610d7d5760405162461bcd60e51b81526020600482015260096024820152683bb937b7339036b4b760b91b6044820152606401610a41565b86518051889160a391610d97918391602090910190614262565b506020828101518051610db09260018501920190614262565b5060408201518051610dcc916002840191602090910190614262565b5060608201518051610de8916003840191602090910190614262565b506080820151600482015560a09091015160059091015585518051879160a991610e19918391602090910190614262565b506020828101518051610e329260018501920190614262565b5060408201518051610e4e916002840191602090910190614262565b5060608201518051610e6a916003840191602090910190614262565b50608082810151600483015560a0909201516005909101556040805180830182528781526020808201889052818301879052606091820186905260af89905560b088905560b187905560b286905560b38590556097805460ff191660011790558251938401835288845283018790528282018690528201849052517f602b6148a680b6852202136cd0ed63398bb67e9b54ab8248ddcf8cc87928c24991610f109161480d565b60405180910390a17f7dc32c0d4744428a5321dfa2ba3882a2f7506b6484e465132ecabd335cdb29798787604051610f49929190614f46565b60405180910390a150505050505050565b6000818152609d6020526040902060609061094190612f87565b6000610f7e612b9c565b9050610f898561302e565b610f91613016565b610fad5760405162461bcd60e51b8152600401610a4190614e2c565b6000858152609f602052604090205460ff1615610fdc5760405162461bcd60e51b8152600401610a4190614f74565b806001600160a01b03166318d928316040518163ffffffff1660e01b8152600401602060405180830381865afa15801561101a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061103e9190614f96565b1561105b5760405162461bcd60e51b8152600401610a4190614fb3565b60006001600160a01b0316846001600160a01b03166396336b306040518163ffffffff1660e01b8152600401602060405180830381865afa1580156110a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110c89190614fd3565b6001600160a01b0316036110ee5760405162461bcd60e51b8152600401610a4190614ff0565b7300379dd90b2a337c4652e286e4fbceadef940a21634c5ab701609c609d604051806101400160405280611120612c39565b6001600160a01b03168152602001866001600160a01b031681526020018a8152602001896001600160a01b03168152602001888152602001878152602001609a548152602001609b54815260200160a260008c815260200190815260200160002054815260200160af6040518060800160405290816000820154815260200160018201548152602001600282015481526020016003820154815250508152506040518463ffffffff1660e01b81526004016111dd93929190615013565b60006040518083038186803b1580156111f557600080fd5b505af4158015611209573d6000803e3d6000fd5b5050505061122f611218612c39565b8587604051806020016040528060008152506133bd565b6000858152609f60209081526040808320805460ff1916600117905560b482528083206001600160a01b03881680855290835292819020869055805188815291820192909252908101849052606081018390527f3be07f3d969f411bb95cc5b5af0235965db26ca648e7df2d84a0424d0086aabf906080015b60405180910390a15050505050565b610b8383838360405180602001604052806000815250612425565b6000610b9a610d3160017f812a673dfca07956350df10f8a654925f561d7a0da09bdbe79e653939a14d9f1614eb0565b600061094182612c1c565b600061131860355490565b821061137b5760405162461bcd60e51b815260206004820152602c60248201527f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60448201526b7574206f6620626f756e647360a01b6064820152608401610a41565b6035828154811061138e5761138e6150e0565b90600052602060002001549050919050565b3330146113ef5760405162461bcd60e51b815260206004820152601b60248201527f496e637265617365207265766973696f6e20666f7262696464656e00000000006044820152606401610a41565b600061141f610d3160017f22573091f17911fb166032a3d9e0554aa73d31b7b7ddea4a4dd2995650af84bd614eb0565b61142a9060016150f6565b905061145e8161145b60017f22573091f17911fb166032a3d9e0554aa73d31b7b7ddea4a4dd2995650af84bd614eb0565b55565b61148d8261145b60017fbfaaa2fb63266ff27c2da975f5894955056f50419af651a81f6c5060581857e4614eb0565b604080518281526001600160a01b03841660208201527ff27e2ef832a4eb8ed8ec553b875eecd44764cda95b1c24170e281539e0a869c891015b60405180910390a15050565b6001600160a01b031673d8253782c45a12053594b9deb72d8e8ab2fca54c1490565b6000908152600360205260409020546001600160a01b031690565b61153b6040518060800160405280600081526020016000815260200160008152602001600081525090565b506040805160808101825260af54815260b054602082015260b1549181019190915260b254606082015290565b6115706133f0565b61157c88888b89613469565b60978054610100600160a81b0319166101006001600160a01b038816021790556098849055609983905581600b8111156115b8576115b8614506565b609a55609b8190556115c86134c5565b505050505050505050565b6115db613313565b600081815260a1602052604090206115f38382615159565b507f29f443087b03c724ded11254c74c7211dcb5e505b280d10747d67f3a0227879082826040516114c7929190615218565b6060610b9a6134e1565b60006001600160a01b03821661169a5760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b6064820152608401610a41565b506001600160a01b031660009081526004602052604090205490565b6000610b9a610d3160017f22573091f17911fb166032a3d9e0554aa73d31b7b7ddea4a4dd2995650af84bd614eb0565b60608061171b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b600084815260da6020526040902061173290612f87565b600085815260db6020526040902061174990612f87565b600095865260dc6020908152604096879020875160a08101895281548152600182015492810192909252600281015497820197909752600387015460608201526004909601546080870152909590949350915050565b60006117a9612b9c565b90506117b4816134f0565b6000878152609f602052604090205460ff1680156117f35750600087815260b4602090815260408083206001600160a01b038a16845290915290205485145b61182b5760405162461bcd60e51b815260206004820152600960248201526808595c5d5a5c1c195960ba1b6044820152606401610a41565b609a54600081900361186c5760405162461bcd60e51b815260206004820152600a602482015269636f6e73756d61626c6560b01b6044820152606401610a41565b6000826001600160a01b0316628e96916040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118cf9190614fd3565b604051630c6222cb60e01b81526001600160a01b038a81166004830152602482018a9052604482018590526064820189905230608483015260a482018c9052600060c483015291925090821690630c6222cb9060e401600060405180830381600087803b15801561193f57600080fd5b505af1158015611953573d6000803e3d6000fd5b50505050831561196d57600089815260a260205260408120555b806001600160a01b03166324a0b0356040518060a001604052808b6001600160a01b031681526020018a81526020016119b7609c60008f8152602001908152602001600020612f87565b8152600060208201819052604091820152516001600160e01b031960e084901b1681526119e7919060040161523a565b600060405180830381600087803b158015611a0157600080fd5b505af1158015611a15573d6000803e3d6000fd5b50505050806001600160a01b03166324a0b0356040518060a001604052808b6001600160a01b031681526020018a8152602001611a63609d60008f8152602001908152602001600020612f87565b8152600160208201526000604091820152516001600160e01b031960e084901b168152611a93919060040161523a565b600060405180830381600087803b158015611aad57600080fd5b505af1158015611ac1573d6000803e3d6000fd5b50505060008a8152609f60209081526040808320805460ff1916905560b482528083206001600160a01b038d168452825280832083905580519182019052908152611b129150899087908c906133bd565b604080518a81526001600160a01b038a811660208301528183018a9052606082018990528716608082015290517fc64e9512c33531b1b4767eb306c4690a87eb36bc541a383fa538499648e9a9f49181900360a00190a1505050505050505050565b60606002805461095690614df2565b6000611b8d612b9c565b9050611b988461302e565b611ba0613016565b611bbc5760405162461bcd60e51b8152600401610a4190614e2c565b806001600160a01b03166318d928316040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bfa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c1e9190614f96565b15611c3b5760405162461bcd60e51b8152600401610a4190614fb3565b6000816001600160a01b0316628e96916040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c9e9190614fd3565b60405163223e40cf60e01b81526001600160a01b038083166004830152861660248201526044810185905260af54606482015260b054608482015260b15460a482015260b25460c48201529091507300379dd90b2a337c4652e286e4fbceadef940a219063223e40cf9060e40160006040518083038186803b158015611d2357600080fd5b505af4158015611d37573d6000803e3d6000fd5b505060405163138e914f60e11b81526001600160a01b038781166004830152602482018790523060448301528416925063271d229e9150606401600060405180830381600087803b158015611d8b57600080fd5b505af1158015611d9f573d6000803e3d6000fd5b505050600086815260dc60205260409081902090516381b182cd60e01b81526001600160a01b038781166004808401919091526024830188905283546044840152600180850154606485015260028501546084850152600385015460a485015293015460c483015260e482019290925290831691506381b182cd9061010401600060405180830381600087803b158015611e3857600080fd5b505af1158015611e4c573d6000803e3d6000fd5b50505050806001600160a01b03166324a0b0356040518060a00160405280876001600160a01b03168152602001868152602001611e9a60da60008b8152602001908152602001600020612f87565b8152600160208201819052604091820152516001600160e01b031960e084901b168152611eca919060040161523a565b600060405180830381600087803b158015611ee457600080fd5b505af1158015611ef8573d6000803e3d6000fd5b50505050806001600160a01b03166324a0b0356040518060a00160405280876001600160a01b03168152602001868152602001611f4660db60008b8152602001908152602001600020612f87565b8152600060208201526001604091820152516001600160e01b031960e084901b168152611f76919060040161523a565b600060405180830381600087803b158015611f9057600080fd5b505af1158015611fa4573d6000803e3d6000fd5b50505050611fb185613071565b604080518681526001600160a01b03861660208201529081018490527f8f0c0d91ba98d7745b984165ac65d4e188c2455ccc13d5155c860f0d9246a815906060016112a8565b6000610b9a610d3160017fbfaaa2fb63266ff27c2da975f5894955056f50419af651a81f6c5060581857e4614eb0565b6000612031612b9c565b9050806001600160a01b03166325a6c51d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612071573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120959190614fd3565b60405163f2f4a8af60e01b81523360048201526001600160a01b03919091169063f2f4a8af90602401602060405180830381865afa1580156120db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120ff9190614f96565b8061217c5750336001600160a01b0316816001600160a01b031663524a562f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561214d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121719190614fd3565b6001600160a01b0316145b8061219757503361218c836114f5565b6001600160a01b0316145b6121b35760405162461bcd60e51b8152600401610a4190614ff0565b6000828152609f602052604090205460ff16156121e25760405162461bcd60e51b8152600401610a4190614f74565b6121eb82613071565b5050565b6121f7613313565b609b8190556040518181527f7ef6d608ec96a3c1d48de6ceb0c0bd19618f4b93f0095308c79cd6ca4b51b187906020015b60405180910390a150565b61223b613313565b60666122478282615159565b507f87cdeaffd8e70903d6ce7cc983fac3b09ca79e83818124c98e47a1d70f8027d68160405161222891906143d7565b61227f6135e6565b6001600160a01b0381166122c75760405162461bcd60e51b815260206004820152600f60248201526e2d32b9379031b7b73a3937b63632b960891b6044820152606401610a41565b6122f68161145b60017f5165972ef41194f06c5007493031d0b927c20741adcb74403b954009fd2c3618614eb0565b6123254261145b60017f6f55f470bdc9cb5f04223fd822021061668e4dccb43e8727b295106dc9769c8b614eb0565b6123544361145b60017f812a673dfca07956350df10f8a654925f561d7a0da09bdbe79e653939a14d9f1614eb0565b604080516001600160a01b038316815242602082015243918101919091527f1a2dd071001ebf6e03174e3df5b305795a4ad5d41d8fdb9ba41dbbe23671342690606001612228565b6121eb6123a7612c39565b838361362c565b6123b6613313565b60008281526067602052604090206123ce8282615159565b507ff32b5bcd021361ac825fde09ff2121b0e5783f9f993196194fab9471799e54dd82826040516114c7929190615296565b600061240a612b9c565b6001600160a01b0316826001600160a01b0316149050919050565b612436612430612c39565b836130aa565b6124525760405162461bcd60e51b8152600401610a4190614e49565b610c2d848484846133bd565b612466613313565b81518051839160dd91612480918391602090910190614262565b5060208281015180516124999260018501920190614262565b50604082015180516124b59160028401916020909101906142ad565b5050815160e05550602081015160e15560408082015160e255606082015160e355608082015160e45560d9805460ff19166001179055517fc8e30ad432142bd0dfb602223be2c9714576cc124dbcee021719e2f29cdaf0b5906114c790849084906152af565b600080612526612b9c565b9050612531816134f0565b6000816001600160a01b031663fcad8c786040518163ffffffff1660e01b8152600401602060405180830381865afa158015612571573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125959190614fd3565b600086815260a2602052604080822054609954609a54925163b3e13ad160e01b8152600481018a9052602481019290925260448201526064810191909152919250906001600160a01b0383169063b3e13ad190608401602060405180830381865afa158015612608573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061262c9190615375565b600087815260a2602090815260409182902083905581518981529081018390529192507feb46efb0c7198679c79853a979eb80da18892986f9942ca8873ed7c4cefb5975910160405180910390a195945050505050565b60606065548211156126c05760405162461bcd60e51b815260206004820152600660248201526508595e1a5cdd60d21b6044820152606401610a41565b600082815260676020526040812080546126d990614df2565b80601f016020809104026020016040519081016040528092919081815260200182805461270590614df2565b80156127525780601f1061272757610100808354040283529160200191612752565b820191906000526020600020905b81548152906001019060200180831161273557829003601f168201915b5050505050905080516000146127685792915050565b6000612773846136fa565b805190915015612784579392505050565b61278c6134e1565b949350505050565b600061279e612b9c565b90506127a8613016565b6127c45760405162461bcd60e51b8152600401610a4190614e2c565b6127cd8361302e565b6127d68261302e565b609b5460008190036128185760405162461bcd60e51b815260206004820152600b60248201526a216475726162696c69747960a81b6044820152606401610a41565b6000848152609f602052604090205460ff1615801561284657506000838152609f602052604090205460ff16155b6128625760405162461bcd60e51b8152600401610a4190614f74565b816001600160a01b03166318d928316040518163ffffffff1660e01b8152600401602060405180830381865afa1580156128a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128c49190614f96565b156128e15760405162461bcd60e51b8152600401610a4190614fb3565b6128ea83613071565b7300379dd90b2a337c4652e286e4fbceadef940a2163806f099d61290c612b9c565b60975461010090046001600160a01b03166001612927612c39565b600a609854612936919061538e565b6040518663ffffffff1660e01b81526004016129569594939291906153b0565b60006040518083038186803b15801561296e57600080fd5b505af4158015612982573d6000803e3d6000fd5b505050600085815260a2602090815260409182902084905581518781529081018690527ffc4ee3fbea4df52f867d98cb796e03a5ae97157fa0c2372c6f61d4336543a7ee925001610c24565b60006129d8612b9c565b6001600160a01b031663fcad8c786040518163ffffffff1660e01b8152600401602060405180830381865afa158015612a15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a399190614fd3565b6001600160a01b03166351c9120d612a62609c6000868152602001908152602001600020612f87565b609b546040518363ffffffff1660e01b8152600401612a829291906153f9565b602060405180830381865afa158015612a9f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109419190615375565b6000816001600160a01b0316612ad7612b9c565b6001600160a01b0316635aa6e6756040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b14573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b389190614fd3565b6001600160a01b03161492915050565b6000826001600160a01b0316612b5d836114f5565b6001600160a01b0316149392505050565b6001600160a01b03918216600090815260066020908152604080832093909416825291909152205460ff1690565b6000610b9a610d3160017f5165972ef41194f06c5007493031d0b927c20741adcb74403b954009fd2c3618614eb0565b60006001600160e01b031982166380ac58cd60e01b1480612bfd57506001600160e01b03198216635b5e139f60e01b145b8061094157506301ffc9a760e01b6001600160e01b0319831614610941565b6000908152600360205260409020546001600160a01b0316151590565b6000612c44336114d3565b15612c56575060131936013560601c90565b503390565b600081815260056020526040902080546001600160a01b0319166001600160a01b0384169081179091558190612c90826114f5565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b612cd1612b9c565b604051633ec963c960e01b81523360048201526001600160a01b039190911690633ec963c990602401602060405180830381865afa158015612d17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d3b9190614f96565b612d725760405162461bcd60e51b815260206004820152600860248201526710b23ab733b2b7b760c11b6044820152606401610a41565b565b6000612d7e6137a9565b9050612d91612d8b612c39565b826137c2565b6000612d9b612b9c565b6001600160a01b031663fcad8c786040518163ffffffff1660e01b8152600401602060405180830381865afa158015612dd8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dfc9190614fd3565b6000838152609c602052604080822060b354915163fe1a90f160e01b815293945091927300379dd90b2a337c4652e286e4fbceadef940a219263fe1a90f192612e4d9287929160a3916004016154b7565b602060405180830381865af4158015612e6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e8e9190615375565b6000848152609d602052604090819020905163fe1a90f160e01b81529192507300379dd90b2a337c4652e286e4fbceadef940a219163fe1a90f191612ede9186919060a9906001906004016154b7565b602060405180830381865af4158015612efb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f1f9190615375565b506000838152609e60209081526040808320849055609b5460a290925290912055612f49836137dc565b60408051848152602081018390527f8a9dcf4e150b1153011b29fec302d5be0c13e84fa8f56ab78587f778a32a90dd910160405180910390a1505090565b60606000612f948361383e565b60408051602b808252610580820190925291925060009190602082016105608036833701905050905060005b8281101561300e57600080612fd58784613849565b915091506000811215612fe6575060005b80848381518110612ff957612ff96150e0565b60209081029190910101525050600101612fc0565b509392505050565b6000613021336114d3565b80610b9a57505032331490565b613036612c39565b6001600160a01b0316613048826114f5565b6001600160a01b03161461306e5760405162461bcd60e51b8152600401610a4190614ff0565b50565b61307a81613865565b6040518181527ff15a2816d9b33bd70f4b57d8cfbd7ee75d3231d8c52a030fef8a86fb4adfe94790602001612228565b60006130b582612c1c565b6131165760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b6064820152608401610a41565b6000613121836114f5565b9050806001600160a01b0316846001600160a01b0316148061315c5750836001600160a01b0316613151846109d9565b6001600160a01b0316145b8061278c575061278c8185612b6e565b826001600160a01b031661317f826114f5565b6001600160a01b0316146131e35760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201526437bbb732b960d91b6064820152608401610a41565b6001600160a01b0382166132455760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610a41565b61325083838361390c565b61325b600082612c5b565b6001600160a01b0383166000908152600460205260408120805460019290613284908490614eb0565b90915550506001600160a01b03821660009081526004602052604081208054600192906132b29084906150f6565b909155505060008181526003602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b61331b612b9c565b604051631430d62960e21b81523360048201526001600160a01b0391909116906350c358a490602401602060405180830381865afa158015613361573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133859190614f96565b612d725760405162461bcd60e51b815260206004820152600960248201526810b232b83637bcb2b960b91b6044820152606401610a41565b6133c884848461316c565b6133d484848484613946565b610c2d5760405162461bcd60e51b8152600401610a41906154ef565b600054610100900460ff1661340b5760005460ff161561340f565b303b155b6134495760405162461bcd60e51b815260206004820152600b60248201526a1a5b9a5d1a585b1a5e995960aa1b6044820152606401610a41565b600054610100900460ff16612d72576000805461ffff1916610101179055565b6134716135e6565b61347b8484613a4e565b61348482612277565b600160655560666134958282615159565b507f87cdeaffd8e70903d6ce7cc983fac3b09ca79e83818124c98e47a1d70f8027d681604051610c2491906143d7565b600054610100900460ff16612d72576000805461ff0019169055565b60606066805461095690614df2565b60405163ba79bf5f60e01b81523360048201526001600160a01b0382169063ba79bf5f90602401602060405180830381865afa158015613534573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135589190614f96565b806135ca5750336001600160a01b0316816001600160a01b031663524a562f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156135a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130489190614fd3565b61306e5760405162461bcd60e51b8152600401610a4190614ff0565b600054610100900460ff16612d725760405162461bcd60e51b815260206004820152600c60248201526b696e697469616c697a696e6760a01b6044820152606401610a41565b816001600160a01b0316836001600160a01b03160361368d5760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610a41565b6001600160a01b03838116600081815260066020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b6000818152609e6020908152604080832054835260a1909152902080546060919061372490614df2565b80601f016020809104026020016040519081016040528092919081815260200182805461375090614df2565b801561379d5780601f106137725761010080835404028352916020019161379d565b820191906000526020600020905b81548152906001019060200180831161378057829003601f168201915b50505050509050919050565b6065546000906137ba8160016150f6565b606555919050565b6121eb828260405180602001604052806000815250613a60565b600081815260da6020908152604080832060db909252909120613800919083613a93565b600091825260dc6020908152604092839020825181559082015160018201559181015160028301556060810151600383015560800151600490910155565b600061094182613dba565b60008080806138588686613dc5565b9097909650945050505050565b6000613870826114f5565b905061387e8160008461390c565b613889600083612c5b565b6001600160a01b03811660009081526004602052604081208054600192906138b2908490614eb0565b909155505060008281526003602052604080822080546001600160a01b0319169055518391906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b6000818152609f602052604090205460ff161561393b5760405162461bcd60e51b8152600401610a4190614f74565b610b83838383613df0565b60006001600160a01b0384163b15613a4357836001600160a01b031663150b7a0261396f612c39565b8786866040518563ffffffff1660e01b81526004016139919493929190615541565b6020604051808303816000875af19250505080156139cc575060408051601f3d908101601f191682019092526139c99181019061557e565b60015b613a29573d8080156139fa576040519150601f19603f3d011682016040523d82523d6000602084013e6139ff565b606091505b508051600003613a215760405162461bcd60e51b8152600401610a41906154ef565b805181602001fd5b6001600160e01b031916630a85bd0160e11b14905061278c565b506001949350505050565b613a566135e6565b6121eb8282613ea8565b613a6a8383613ec9565b613a776000848484613946565b610b835760405162461bcd60e51b8152600401610a41906154ef565b613ac56040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b6000613acf612b9c565b6001600160a01b031663fcad8c786040518163ffffffff1660e01b8152600401602060405180830381865afa158015613b0c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b309190614fd3565b9050600080826001600160a01b0316635cb8980f60dd6040518263ffffffff1660e01b8152600401613b62919061559b565b600060405180830381865afa158015613b7f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613ba79190810190615dc3565b91509150600080846001600160a01b0316635027f3848560b3546040518363ffffffff1660e01b8152600401613bde929190615e1c565b6000604051808303816000875af1158015613bfd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613c259190810190615e2f565b509150915060005b8151811015613c91576000828281518110613c4a57613c4a6150e0565b602002602001015190506000848381518110613c6857613c686150e0565b6020026020010151905081600014613c8757613c858c8284614008565b505b5050600101613c2d565b5060b354604051631409fce160e21b815260009182916001600160a01b03891691635027f38491613cc6918991600401615e1c565b6000604051808303816000875af1158015613ce5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613d0d9190810190615e2f565b509150915060005b8151811015613d79576000828281518110613d3257613d326150e0565b602002602001015190506000848381518110613d5057613d506150e0565b6020026020010151905081600014613d6f57613d6d8d8284614008565b505b5050600101613d15565b50506040805160a08101825260e054815260e154602082015260e2549181019190915260e354606082015260e45460808201529a9950505050505050505050565b600061094182614015565b60008080613dd3858561401f565b600081815260029690960160205260409095205494959350505050565b6001600160a01b038316613e4b57613e4681603580546000838152603660205260408120829055600182018355919091527fcfa4bec1d3298408bb5afcfcd9c430549c5b31f8aa5c5848151c0a55f473c34d0155565b613e6e565b816001600160a01b0316836001600160a01b031614613e6e57613e6e8382614032565b6001600160a01b038216613e8557610b83816140cf565b826001600160a01b0316826001600160a01b031614610b8357610b83828261417e565b613eb06135e6565b6001613ebc8382615159565b506002610b838282615159565b6001600160a01b038216613f1f5760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610a41565b613f2881612c1c565b15613f755760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610a41565b613f816000838361390c565b6001600160a01b0382166000908152600460205260408120805460019290613faa9084906150f6565b909155505060008181526003602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b600061278c8484846141c2565b6000610941825490565b600061402b83836141df565b9392505050565b6000600161403f8461162f565b6140499190614eb0565b60008381526034602052604090205490915080821461409c576001600160a01b03841660009081526033602090815260408083208584528252808320548484528184208190558352603490915290208190555b5060009182526034602090815260408084208490556001600160a01b039094168352603381528383209183525290812055565b6035546000906140e190600190614eb0565b60008381526036602052604081205460358054939450909284908110614109576141096150e0565b90600052602060002001549050806035838154811061412a5761412a6150e0565b600091825260208083209091019290925582815260369091526040808220849055858252812055603580548061416257614162615eaa565b6001900381819060005260206000200160009055905550505050565b60006141898361162f565b6001600160a01b039093166000908152603360209081526040808320868452825280832085905593825260349052919091209190915550565b6000828152600284016020526040812082905561278c8484614209565b60008260000182815481106141f6576141f66150e0565b9060005260206000200154905092915050565b600081815260018301602052604081205461402b9084908490849061425a57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610941565b506000610941565b82805482825590600052602060002090810192821561429d579160200282015b8281111561429d578251825591602001919060010190614282565b506142a9929150614349565b5090565b82805482825590600052602060002090601f0160209004810192821561429d5791602002820160005b8382111561431357835183826101000a81548160ff02191690831515021790555092602001926001016020816000010492830192600103026142d6565b80156143405782816101000a81549060ff0219169055600101602081600001049283019260010302614313565b50506142a99291505b5b808211156142a9576000815560010161434a565b6001600160e01b03198116811461306e57600080fd5b60006020828403121561438657600080fd5b813561402b8161435e565b6000815180845260005b818110156143b75760208185018101518683018201520161439b565b506000602082860101526020601f19601f83011685010191505092915050565b60208152600061402b6020830184614391565b6000602082840312156143fc57600080fd5b5035919050565b6001600160a01b038116811461306e57600080fd5b803561442381614403565b919050565b6000806040838503121561443b57600080fd5b823561444681614403565b946020939093013593505050565b60008151808452602080850194506020840160005b8381101561448557815187529582019590820190600101614469565b509495945050505050565b60208152600061402b6020830184614454565b600080604083850312156144b657600080fd5b50508035926020909101359150565b6000806000606084860312156144da57600080fd5b83356144e581614403565b925060208401356144f581614403565b929592945050506040919091013590565b634e487b7160e01b600052602160045260246000fd5b602081016005831061453057614530614506565b91905290565b634e487b7160e01b600052604160045260246000fd5b60405160c081016001600160401b038111828210171561456e5761456e614536565b60405290565b604051606081016001600160401b038111828210171561456e5761456e614536565b604051601f8201601f191681016001600160401b03811182821017156145be576145be614536565b604052919050565b60006001600160401b038211156145df576145df614536565b5060051b60200190565b600082601f8301126145fa57600080fd5b8135602061460f61460a836145c6565b614596565b8083825260208201915060208460051b87010193508684111561463157600080fd5b602086015b8481101561464d5780358352918301918301614636565b509695505050505050565b600060c0828403121561466a57600080fd5b61467261454c565b905081356001600160401b038082111561468b57600080fd5b614697858386016145e9565b835260208401359150808211156146ad57600080fd5b6146b9858386016145e9565b602084015260408401359150808211156146d257600080fd5b6146de858386016145e9565b604084015260608401359150808211156146f757600080fd5b50614704848285016145e9565b6060830152506080820135608082015260a082013560a082015292915050565b600080600080600080600060e0888a03121561473f57600080fd5b87356001600160401b038082111561475657600080fd5b6147628b838c01614658565b985060208a013591508082111561477857600080fd5b506147858a828b01614658565b979a9799505050506040860135956060810135956080820135955060a0820135945060c09091013592509050565b600080600080608085870312156147c957600080fd5b8435935060208501356147db81614403565b93969395505050506040820135916060013590565b60006020828403121561480257600080fd5b813561402b81614403565b8151815260208083015190820152604080830151908201526060808301519082015260808101610941565b60006001600160401b0383111561485157614851614536565b614864601f8401601f1916602001614596565b905082815283838301111561487857600080fd5b828260208301376000602084830101529392505050565b600082601f8301126148a057600080fd5b61402b83833560208501614838565b8035600c811061442357600080fd5b60008060008060008060008060006101208a8c0312156148dd57600080fd5b6148e68a614418565b985060208a01356001600160401b038082111561490257600080fd5b61490e8d838e0161488f565b995060408c013591508082111561492457600080fd5b6149308d838e0161488f565b985060608c013591508082111561494657600080fd5b506149538c828d0161488f565b96505061496260808b01614418565b945060a08a0135935060c08a0135925061497e60e08b016148af565b91506101008a013590509295985092959850929598565b600080604083850312156149a857600080fd5b82356001600160401b038111156149be57600080fd5b6149ca8582860161488f565b95602094909401359450505050565b60e0815260006149ec60e0830186614454565b82810360208401526149fe8186614454565b91505061278c604083018480518252602081015160208301526040810151604083015260608101516060830152608081015160808301525050565b801515811461306e57600080fd5b60008060008060008060c08789031215614a6057600080fd5b863595506020870135614a7281614403565b945060408701359350606087013592506080870135614a9081614403565b915060a0870135614aa081614a39565b809150509295509295509295565b600080600060608486031215614ac357600080fd5b8335925060208401356144f581614403565b600060208284031215614ae757600080fd5b81356001600160401b03811115614afd57600080fd5b61278c8482850161488f565b60008060408385031215614b1c57600080fd5b8235614b2781614403565b91506020830135614b3781614a39565b809150509250929050565b60008060408385031215614b5557600080fd5b823591506020830135614b3781614403565b60008060408385031215614b7a57600080fd5b8235915060208301356001600160401b03811115614b9757600080fd5b614ba38582860161488f565b9150509250929050565b60008060008060808587031215614bc357600080fd5b8435614bce81614403565b93506020850135614bde81614403565b92506040850135915060608501356001600160401b03811115614c0057600080fd5b8501601f81018713614c1157600080fd5b614c2087823560208401614838565b91505092959194509250565b600060a08284031215614c3e57600080fd5b60405160a081018181106001600160401b0382111715614c6057614c60614536565b806040525080915082358152602083013560208201526040830135604082015260608301356060820152608083013560808201525092915050565b60008060c08385031215614cae57600080fd5b82356001600160401b0380821115614cc557600080fd5b9084019060608287031215614cd957600080fd5b614ce1614574565b823582811115614cf057600080fd5b614cfc888286016145e9565b82525060208084013583811115614d1257600080fd5b614d1e898287016145e9565b8284015250604084013583811115614d3557600080fd5b80850194505087601f850112614d4a57600080fd5b83359250614d5a61460a846145c6565b83815260059390931b84018101928181019089851115614d7957600080fd5b948201945b84861015614da0578535614d9181614a39565b82529482019490820190614d7e565b8060408501525050819550614db788828901614c2c565b9450505050509250929050565b60008060408385031215614dd757600080fd5b8235614de281614403565b91506020830135614b3781614403565b600181811c90821680614e0657607f821691505b602082108103614e2657634e487b7160e01b600052602260045260246000fd5b50919050565b602080825260039082015262454f4160e81b604082015260600190565b60208082526031908201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6040820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b8181038181111561094157610941614e9a565b6000815160c08452614ed860c0850182614454565b905060208301518482036020860152614ef18282614454565b91505060408301518482036040860152614f0b8282614454565b91505060608301518482036060860152614f258282614454565b9150506080830151608085015260a083015160a08501528091505092915050565b604081526000614f596040830185614ec3565b8281036020840152614f6b8185614ec3565b95945050505050565b602080825260089082015267195c5d5a5c1c195960c21b604082015260600190565b600060208284031215614fa857600080fd5b815161402b81614a39565b6020808252600690820152651c185d5cd95960d21b604082015260600190565b600060208284031215614fe557600080fd5b815161402b81614403565b6020808252600990820152683337b93134b23232b760b91b604082015260600190565b8381526020810183905281516001600160a01b031660408201526101e0810160208301516001600160a01b0381166060840152506040830151608083015260608301516001600160a01b03811660a084015250608083015160c083015260a083015160e083015260c0830151610100818185015260e0850151915061012082818601528186015161014086015280860151925050506150d7610160840182805182526020810151602083015260408101516040830152606081015160608301525050565b50949350505050565b634e487b7160e01b600052603260045260246000fd5b8082018082111561094157610941614e9a565b601f821115610b83576000816000526020600020601f850160051c810160208610156151325750805b601f850160051c820191505b818110156151515782815560010161513e565b505050505050565b81516001600160401b0381111561517257615172614536565b615186816151808454614df2565b84615109565b602080601f8311600181146151bb57600084156151a35750858301515b600019600386901b1c1916600185901b178555615151565b600085815260208120601f198616915b828110156151ea578886015182559484019460019091019084016151cb565b50858210156152085787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60408152600061522b6040830185614391565b90508260208301529392505050565b6020815260018060a01b038251166020820152602082015160408201526000604083015160a0606084015261527260c0840182614454565b90506060840151151560808401526080840151151560a08401528091505092915050565b82815260406020820152600061278c6040830184614391565b60c0815260008351606060c08401526152cc610120840182614454565b905060208086015160bf19808685030160e08701526152eb8483614454565b604089015187820390920161010088015281518082529184019450600092508301905b808310156153305784511515825293830193600192909201919083019061530e565b50935061536c90508482018680518252602081015160208301526040810151604083015260608101516060830152608081015160808301525050565b50509392505050565b60006020828403121561538757600080fd5b5051919050565b6000826153ab57634e487b7160e01b600052601260045260246000fd5b500490565b6001600160a01b038681168252858116602083015260a0820190600386106153da576153da614506565b8560408401528085166060840152508260808301529695505050505050565b60408152600061522b6040830185614454565b600081548084526020808501945083600052602060002060005b8381101561448557815487529582019560019182019101615426565b60c08252600061545560c084018361540c565b838103602085015261546a816001850161540c565b90508381036040850152615481816002850161540c565b90508381036060850152615498816003850161540c565b6004840154608086015260059093015460a09094019390935250919050565b60018060a01b03851681528360208201526080604082015260006154de6080830185615442565b905082606083015295945050505050565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b6001600160a01b038581168252841660208201526040810183905260806060820181905260009061557490830184614391565b9695505050505050565b60006020828403121561559057600080fd5b815161402b8161435e565b60006020808352606080828501526155b6608085018661540c565b6001601f196040818885030160408901526155d384848b0161540c565b888103929092016060890152600289018054808452600091825260208083209650909301925b80601f8301101561585c57855460ff808216151586526156218a8701828460081c1615159052565b615633858701828460101c1615159052565b615645898701828460181c1615159052565b818a1c81161515608087015260a0615665818801838560281c1615159052565b60c0615679818901848660301c1615159052565b60e061568d818a01858760381c1615159052565b84881c841615156101008a01526156ae6101208a01858760481c1615159052565b6156c26101408a01858760501c1615159052565b6156d66101608a01858760581c1615159052565b848c1c841615156101808a01526156f76101a08a01858760681c1615159052565b61570b6101c08a01858760701c1615159052565b61571f6101e08a01858760781c1615159052565b6157336102008a01858760801c1615159052565b6157476102208a01858760881c1615159052565b61575b6102408a01858760901c1615159052565b61576f6102608a01858760981c1615159052565b84831c841615156102808a01526157906102a08a01858760a81c1615159052565b6157a46102c08a01858760b01c1615159052565b6157b86102e08a01858760b81c1615159052565b84821c841615156103008a01526157d96103208a01858760c81c1615159052565b6157ed6103408a01858760d01c1615159052565b6158016103608a01858760d81c1615159052565b84901c83161515610380890152505060e882901c811615156103a08701526158336103c08701828460f01c1615159052565b506158466103e086018260f81c15159052565b50948401946104009390930192908701906155f9565b85549650808210156158795760ff87161515845292870192908401905b8082101561589b576158928460ff8960081c1615159052565b92870192908401905b808210156158bd576158b48460ff8960101c1615159052565b92870192908401905b808210156158df576158d68460ff8960181c1615159052565b92870192908401905b808210156158fa5786881c60ff161515845292870192908401905b8082101561591c576159138460ff8960281c1615159052565b92870192908401905b8082101561593e576159358460ff8960301c1615159052565b92870192908401905b80821015615960576159578460ff8960381c1615159052565b92870192908401905b80821015615982576159798460ff8960401c1615159052565b92870192908401905b808210156159a45761599b8460ff8960481c1615159052565b92870192908401905b808210156159c6576159bd8460ff8960501c1615159052565b92870192908401905b808210156159e8576159df8460ff8960581c1615159052565b92870192908401905b80821015615a0a57615a018460ff8960601c1615159052565b92870192908401905b80821015615a2c57615a238460ff8960681c1615159052565b92870192908401905b80821015615a4e57615a458460ff8960701c1615159052565b92870192908401905b80821015615a7057615a678460ff8960781c1615159052565b92870192908401905b80821015615a9257615a898460ff8960801c1615159052565b92870192908401905b80821015615ab457615aab8460ff8960881c1615159052565b92870192908401905b80821015615ad657615acd8460ff8960901c1615159052565b92870192908401905b80821015615af857615aef8460ff8960981c1615159052565b92870192908401905b80821015615b1a57615b118460ff8960a01c1615159052565b92870192908401905b80821015615b3c57615b338460ff8960a81c1615159052565b92870192908401905b80821015615b5e57615b558460ff8960b01c1615159052565b92870192908401905b80821015615b8057615b778460ff8960b81c1615159052565b92870192908401905b80821015615ba257615b998460ff8960c01c1615159052565b92870192908401905b80821015615bc457615bbb8460ff8960c81c1615159052565b92870192908401905b80821015615be657615bdd8460ff8960d01c1615159052565b92870192908401905b80821015615c0857615bff8460ff8960d81c1615159052565b92870192908401905b80821015615c2a57615c218460ff8960e01c1615159052565b92870192908401905b80821015615c4c57615c438460ff8960e81c1615159052565b92870192908401905b80821015615c6e57615c658460ff8960f01c1615159052565b92870192908401905b80821015615c8957615c84848860f81c15159052565b928701925b50919998505050505050505050565b600082601f830112615ca957600080fd5b81516020615cb961460a836145c6565b8083825260208201915060208460051b870101935086841115615cdb57600080fd5b602086015b8481101561464d5780518352918301918301615ce0565b600060c08284031215615d0957600080fd5b615d1161454c565b905081516001600160401b0380821115615d2a57600080fd5b615d3685838601615c98565b83526020840151915080821115615d4c57600080fd5b615d5885838601615c98565b60208401526040840151915080821115615d7157600080fd5b615d7d85838601615c98565b60408401526060840151915080821115615d9657600080fd5b50615da384828501615c98565b6060830152506080820151608082015260a082015160a082015292915050565b60008060408385031215615dd657600080fd5b82516001600160401b0380821115615ded57600080fd5b615df986838701615cf7565b93506020850151915080821115615e0f57600080fd5b50614ba385828601615cf7565b60408152600061522b6040830185614ec3565b600080600060608486031215615e4457600080fd5b83516001600160401b0380821115615e5b57600080fd5b615e6787838801615c98565b94506020860151915080821115615e7d57600080fd5b50615e8a86828701615c98565b925050604084015160068110615e9f57600080fd5b809150509250925092565b634e487b7160e01b600052603160045260246000fdfea2646970667358221220c7637622787df9ce503483ee2d3ac40d79b6b54d50f45c00a7de8df04c48540164736f6c63430008170033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106104075760003560e01c80636c0360eb11610220578063b423799e11610130578063d909ea5f116100b8578063f020c59411610087578063f020c594146108b3578063f5470d7b146108f3578063f77c4791146108fc578063fcc78fa514610904578063ffa1ad741461072157600080fd5b8063d909ea5f146108b3578063dee1f0e4146108ba578063e327a6af146108cd578063e985e9c5146108e057600080fd5b8063b9114ce1116100ff578063b9114ce114610854578063bc0d396b14610867578063c87b56dd1461087a578063d1089f331461088d578063d1730f1f146108a057600080fd5b8063b423799e14610805578063b429afeb14610825578063b75f4d8f14610838578063b88d4fde1461084157600080fd5b80639845f17a116101b3578063a0bcfc7f11610182578063a0bcfc7f1461078e578063a0fe0f51146107a1578063a22cb465146107b4578063a5d3bafb146107c7578063a60138a7146107f257600080fd5b80639845f17a1461074d5780639d0bcca0146107605780639d118770146107685780639e2cfb0f1461077b57600080fd5b8063820e5865116101ef578063820e5865146106ec5780638e776f9c1461070e578063936725ec1461072157806395d89b411461074557600080fd5b80636c0360eb146106a957806370a08231146106b1578063718df8fa146106c45780637cc96380146106e457600080fd5b806336c944851161031b5780634f558e79116102ae578063583eab451161027d578063583eab451461063b5780636352211e1461065b57806364f2d7531461066e57806366679a0614610683578063681641381461069657600080fd5b80634f558e79146105ef5780634f6ccce7146106025780634fac6ccd14610615578063572b6c051461062857600080fd5b80633e3008b6116102ea5780633e3008b6146105c157806342842e0e146105d45780634593144c146105e75780634dd8481d1461055e57600080fd5b806336c94485146105895780633710c70a1461059c5780633c97cf60146105af5780633d54e23b146105b857600080fd5b80631f2a5ace1161039e5780632f745c591161036d5780632f745c5914610543578063325a19f11461055657806332d671a11461055e57806335320ba21461058257806335798ed01461058257600080fd5b80631f2a5ace146104ea578063203e597c1461050e57806323b872dd146105215780632e2da5171461053457600080fd5b80631249c58b116103da5780631249c58b1461048957806318160ddd1461049f5780631a8abe9a146104a75780631c001d77146104ca57600080fd5b806301ffc9a71461040c57806306fdde0314610434578063081812fc14610449578063095ea7b314610474575b600080fd5b61041f61041a366004614374565b61091c565b60405190151581526020015b60405180910390f35b61043c610947565b60405161042b91906143d7565b61045c6104573660046143ea565b6109d9565b6040516001600160a01b03909116815260200161042b565b610487610482366004614428565b610a66565b005b610491610b88565b60405190815260200161042b565b603554610491565b61041f6104b53660046143ea565b609f6020526000908152604090205460ff1681565b6104dd6104d83660046143ea565b610b9f565b60405161042b9190614490565b61043c60405180604001604052806005815260200164312e302e3360d81b81525081565b61048761051c3660046144a3565b610bb9565b61048761052f3660046144c5565b610c33565b600460405161042b919061451c565b610491610551366004614428565b610c6b565b610491610d01565b61043c60405180604001604052806005815260200164312e302e3160d81b81525081565b600161041f565b610487610597366004614724565b610d35565b6104dd6105aa3660046143ea565b610f5a565b61049160985481565b61049160995481565b6104876105cf3660046147b3565b610f74565b6104876105e23660046144c5565b6112b7565b6104916112d2565b61041f6105fd3660046143ea565b611302565b6104916106103660046143ea565b61130d565b6104876106233660046147f0565b6113a0565b61041f6106363660046147f0565b6114d3565b6104916106493660046143ea565b609e6020526000908152604090205481565b61045c6106693660046143ea565b6114f5565b610676611510565b60405161042b919061480d565b6104876106913660046148be565b611568565b6104876106a4366004614995565b6115d3565b61043c611625565b6104916106bf3660046147f0565b61162f565b6104916106d23660046143ea565b60a26020526000908152604090205481565b6104916116b6565b6106ff6106fa3660046143ea565b6116e6565b60405161042b939291906149d9565b61048761071c366004614a47565b61179f565b61043c604051806040016040528060058152602001640312e302e360dc1b81525081565b61043c611b74565b61048761075b366004614aae565b611b83565b61045c611ff7565b6104876107763660046143ea565b612027565b6104876107893660046143ea565b6121ef565b61048761079c366004614ad5565b612233565b6104876107af3660046147f0565b612277565b6104876107c2366004614b09565b61239c565b6104916107d5366004614b42565b60b460209081526000928352604080842090915290825290205481565b610487610800366004614b67565b6123ae565b6104916108133660046143ea565b60a06020526000908152604090205481565b61041f6108333660046147f0565b612400565b610491609b5481565b61048761084f366004614bad565b612425565b610487610862366004614c9b565b61245e565b6104916108753660046144a3565b61251b565b61043c6108883660046143ea565b612683565b61048761089b3660046144a3565b612794565b6104916108ae3660046143ea565b6129ce565b600061041f565b61041f6108c83660046147f0565b612ac3565b61041f6108db366004614428565b612b48565b61041f6108ee366004614dc4565b612b6e565b610491609a5481565b61045c612b9c565b60975461045c9061010090046001600160a01b031681565b60006001600160e01b0319821663780e9d6360e01b1480610941575061094182612bcc565b92915050565b60606001805461095690614df2565b80601f016020809104026020016040519081016040528092919081815260200182805461098290614df2565b80156109cf5780601f106109a4576101008083540402835291602001916109cf565b820191906000526020600020905b8154815290600101906020018083116109b257829003601f168201915b5050505050905090565b60006109e482612c1c565b610a4a5760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b60648201526084015b60405180910390fd5b506000908152600560205260409020546001600160a01b031690565b6000610a71826114f5565b9050806001600160a01b0316836001600160a01b031603610ade5760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608401610a41565b806001600160a01b0316610af0612c39565b6001600160a01b03161480610b0c5750610b0c816108ee612c39565b610b795760405162461bcd60e51b815260206004820152603860248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f776044820152771b995c881b9bdc88185c1c1c9bdd995908199bdc88185b1b60421b6064820152608401610a41565b610b838383612c5b565b505050565b6000610b92612cc9565b610b9a612d74565b905090565b6000818152609c6020526040902060609061094190612f87565b610bc1613016565b610bdd5760405162461bcd60e51b8152600401610a4190614e2c565b610be68261302e565b610bef8161302e565b60405162461bcd60e51b815260206004820152600a602482015269636f6e73756d61626c6560b01b6044820152606401610a41565b60405180910390a15b50505050565b610c44610c3e612c39565b826130aa565b610c605760405162461bcd60e51b8152600401610a4190614e49565b610b8383838361316c565b6000610c768361162f565b8210610cd85760405162461bcd60e51b815260206004820152602b60248201527f455243373231456e756d657261626c653a206f776e657220696e646578206f7560448201526a74206f6620626f756e647360a81b6064820152608401610a41565b506001600160a01b03919091166000908152603360209081526040808320938352929052205490565b6000610b9a610d3160017f6f55f470bdc9cb5f04223fd822021061668e4dccb43e8727b295106dc9769c8b614eb0565b5490565b610d3d613313565b6002876080015110610d7d5760405162461bcd60e51b81526020600482015260096024820152683bb937b7339036b4b760b91b6044820152606401610a41565b86518051889160a391610d97918391602090910190614262565b506020828101518051610db09260018501920190614262565b5060408201518051610dcc916002840191602090910190614262565b5060608201518051610de8916003840191602090910190614262565b506080820151600482015560a09091015160059091015585518051879160a991610e19918391602090910190614262565b506020828101518051610e329260018501920190614262565b5060408201518051610e4e916002840191602090910190614262565b5060608201518051610e6a916003840191602090910190614262565b50608082810151600483015560a0909201516005909101556040805180830182528781526020808201889052818301879052606091820186905260af89905560b088905560b187905560b286905560b38590556097805460ff191660011790558251938401835288845283018790528282018690528201849052517f602b6148a680b6852202136cd0ed63398bb67e9b54ab8248ddcf8cc87928c24991610f109161480d565b60405180910390a17f7dc32c0d4744428a5321dfa2ba3882a2f7506b6484e465132ecabd335cdb29798787604051610f49929190614f46565b60405180910390a150505050505050565b6000818152609d6020526040902060609061094190612f87565b6000610f7e612b9c565b9050610f898561302e565b610f91613016565b610fad5760405162461bcd60e51b8152600401610a4190614e2c565b6000858152609f602052604090205460ff1615610fdc5760405162461bcd60e51b8152600401610a4190614f74565b806001600160a01b03166318d928316040518163ffffffff1660e01b8152600401602060405180830381865afa15801561101a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061103e9190614f96565b1561105b5760405162461bcd60e51b8152600401610a4190614fb3565b60006001600160a01b0316846001600160a01b03166396336b306040518163ffffffff1660e01b8152600401602060405180830381865afa1580156110a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110c89190614fd3565b6001600160a01b0316036110ee5760405162461bcd60e51b8152600401610a4190614ff0565b7300379dd90b2a337c4652e286e4fbceadef940a21634c5ab701609c609d604051806101400160405280611120612c39565b6001600160a01b03168152602001866001600160a01b031681526020018a8152602001896001600160a01b03168152602001888152602001878152602001609a548152602001609b54815260200160a260008c815260200190815260200160002054815260200160af6040518060800160405290816000820154815260200160018201548152602001600282015481526020016003820154815250508152506040518463ffffffff1660e01b81526004016111dd93929190615013565b60006040518083038186803b1580156111f557600080fd5b505af4158015611209573d6000803e3d6000fd5b5050505061122f611218612c39565b8587604051806020016040528060008152506133bd565b6000858152609f60209081526040808320805460ff1916600117905560b482528083206001600160a01b03881680855290835292819020869055805188815291820192909252908101849052606081018390527f3be07f3d969f411bb95cc5b5af0235965db26ca648e7df2d84a0424d0086aabf906080015b60405180910390a15050505050565b610b8383838360405180602001604052806000815250612425565b6000610b9a610d3160017f812a673dfca07956350df10f8a654925f561d7a0da09bdbe79e653939a14d9f1614eb0565b600061094182612c1c565b600061131860355490565b821061137b5760405162461bcd60e51b815260206004820152602c60248201527f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60448201526b7574206f6620626f756e647360a01b6064820152608401610a41565b6035828154811061138e5761138e6150e0565b90600052602060002001549050919050565b3330146113ef5760405162461bcd60e51b815260206004820152601b60248201527f496e637265617365207265766973696f6e20666f7262696464656e00000000006044820152606401610a41565b600061141f610d3160017f22573091f17911fb166032a3d9e0554aa73d31b7b7ddea4a4dd2995650af84bd614eb0565b61142a9060016150f6565b905061145e8161145b60017f22573091f17911fb166032a3d9e0554aa73d31b7b7ddea4a4dd2995650af84bd614eb0565b55565b61148d8261145b60017fbfaaa2fb63266ff27c2da975f5894955056f50419af651a81f6c5060581857e4614eb0565b604080518281526001600160a01b03841660208201527ff27e2ef832a4eb8ed8ec553b875eecd44764cda95b1c24170e281539e0a869c891015b60405180910390a15050565b6001600160a01b031673d8253782c45a12053594b9deb72d8e8ab2fca54c1490565b6000908152600360205260409020546001600160a01b031690565b61153b6040518060800160405280600081526020016000815260200160008152602001600081525090565b506040805160808101825260af54815260b054602082015260b1549181019190915260b254606082015290565b6115706133f0565b61157c88888b89613469565b60978054610100600160a81b0319166101006001600160a01b038816021790556098849055609983905581600b8111156115b8576115b8614506565b609a55609b8190556115c86134c5565b505050505050505050565b6115db613313565b600081815260a1602052604090206115f38382615159565b507f29f443087b03c724ded11254c74c7211dcb5e505b280d10747d67f3a0227879082826040516114c7929190615218565b6060610b9a6134e1565b60006001600160a01b03821661169a5760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b6064820152608401610a41565b506001600160a01b031660009081526004602052604090205490565b6000610b9a610d3160017f22573091f17911fb166032a3d9e0554aa73d31b7b7ddea4a4dd2995650af84bd614eb0565b60608061171b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b600084815260da6020526040902061173290612f87565b600085815260db6020526040902061174990612f87565b600095865260dc6020908152604096879020875160a08101895281548152600182015492810192909252600281015497820197909752600387015460608201526004909601546080870152909590949350915050565b60006117a9612b9c565b90506117b4816134f0565b6000878152609f602052604090205460ff1680156117f35750600087815260b4602090815260408083206001600160a01b038a16845290915290205485145b61182b5760405162461bcd60e51b815260206004820152600960248201526808595c5d5a5c1c195960ba1b6044820152606401610a41565b609a54600081900361186c5760405162461bcd60e51b815260206004820152600a602482015269636f6e73756d61626c6560b01b6044820152606401610a41565b6000826001600160a01b0316628e96916040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118cf9190614fd3565b604051630c6222cb60e01b81526001600160a01b038a81166004830152602482018a9052604482018590526064820189905230608483015260a482018c9052600060c483015291925090821690630c6222cb9060e401600060405180830381600087803b15801561193f57600080fd5b505af1158015611953573d6000803e3d6000fd5b50505050831561196d57600089815260a260205260408120555b806001600160a01b03166324a0b0356040518060a001604052808b6001600160a01b031681526020018a81526020016119b7609c60008f8152602001908152602001600020612f87565b8152600060208201819052604091820152516001600160e01b031960e084901b1681526119e7919060040161523a565b600060405180830381600087803b158015611a0157600080fd5b505af1158015611a15573d6000803e3d6000fd5b50505050806001600160a01b03166324a0b0356040518060a001604052808b6001600160a01b031681526020018a8152602001611a63609d60008f8152602001908152602001600020612f87565b8152600160208201526000604091820152516001600160e01b031960e084901b168152611a93919060040161523a565b600060405180830381600087803b158015611aad57600080fd5b505af1158015611ac1573d6000803e3d6000fd5b50505060008a8152609f60209081526040808320805460ff1916905560b482528083206001600160a01b038d168452825280832083905580519182019052908152611b129150899087908c906133bd565b604080518a81526001600160a01b038a811660208301528183018a9052606082018990528716608082015290517fc64e9512c33531b1b4767eb306c4690a87eb36bc541a383fa538499648e9a9f49181900360a00190a1505050505050505050565b60606002805461095690614df2565b6000611b8d612b9c565b9050611b988461302e565b611ba0613016565b611bbc5760405162461bcd60e51b8152600401610a4190614e2c565b806001600160a01b03166318d928316040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bfa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c1e9190614f96565b15611c3b5760405162461bcd60e51b8152600401610a4190614fb3565b6000816001600160a01b0316628e96916040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c9e9190614fd3565b60405163223e40cf60e01b81526001600160a01b038083166004830152861660248201526044810185905260af54606482015260b054608482015260b15460a482015260b25460c48201529091507300379dd90b2a337c4652e286e4fbceadef940a219063223e40cf9060e40160006040518083038186803b158015611d2357600080fd5b505af4158015611d37573d6000803e3d6000fd5b505060405163138e914f60e11b81526001600160a01b038781166004830152602482018790523060448301528416925063271d229e9150606401600060405180830381600087803b158015611d8b57600080fd5b505af1158015611d9f573d6000803e3d6000fd5b505050600086815260dc60205260409081902090516381b182cd60e01b81526001600160a01b038781166004808401919091526024830188905283546044840152600180850154606485015260028501546084850152600385015460a485015293015460c483015260e482019290925290831691506381b182cd9061010401600060405180830381600087803b158015611e3857600080fd5b505af1158015611e4c573d6000803e3d6000fd5b50505050806001600160a01b03166324a0b0356040518060a00160405280876001600160a01b03168152602001868152602001611e9a60da60008b8152602001908152602001600020612f87565b8152600160208201819052604091820152516001600160e01b031960e084901b168152611eca919060040161523a565b600060405180830381600087803b158015611ee457600080fd5b505af1158015611ef8573d6000803e3d6000fd5b50505050806001600160a01b03166324a0b0356040518060a00160405280876001600160a01b03168152602001868152602001611f4660db60008b8152602001908152602001600020612f87565b8152600060208201526001604091820152516001600160e01b031960e084901b168152611f76919060040161523a565b600060405180830381600087803b158015611f9057600080fd5b505af1158015611fa4573d6000803e3d6000fd5b50505050611fb185613071565b604080518681526001600160a01b03861660208201529081018490527f8f0c0d91ba98d7745b984165ac65d4e188c2455ccc13d5155c860f0d9246a815906060016112a8565b6000610b9a610d3160017fbfaaa2fb63266ff27c2da975f5894955056f50419af651a81f6c5060581857e4614eb0565b6000612031612b9c565b9050806001600160a01b03166325a6c51d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612071573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120959190614fd3565b60405163f2f4a8af60e01b81523360048201526001600160a01b03919091169063f2f4a8af90602401602060405180830381865afa1580156120db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120ff9190614f96565b8061217c5750336001600160a01b0316816001600160a01b031663524a562f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561214d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121719190614fd3565b6001600160a01b0316145b8061219757503361218c836114f5565b6001600160a01b0316145b6121b35760405162461bcd60e51b8152600401610a4190614ff0565b6000828152609f602052604090205460ff16156121e25760405162461bcd60e51b8152600401610a4190614f74565b6121eb82613071565b5050565b6121f7613313565b609b8190556040518181527f7ef6d608ec96a3c1d48de6ceb0c0bd19618f4b93f0095308c79cd6ca4b51b187906020015b60405180910390a150565b61223b613313565b60666122478282615159565b507f87cdeaffd8e70903d6ce7cc983fac3b09ca79e83818124c98e47a1d70f8027d68160405161222891906143d7565b61227f6135e6565b6001600160a01b0381166122c75760405162461bcd60e51b815260206004820152600f60248201526e2d32b9379031b7b73a3937b63632b960891b6044820152606401610a41565b6122f68161145b60017f5165972ef41194f06c5007493031d0b927c20741adcb74403b954009fd2c3618614eb0565b6123254261145b60017f6f55f470bdc9cb5f04223fd822021061668e4dccb43e8727b295106dc9769c8b614eb0565b6123544361145b60017f812a673dfca07956350df10f8a654925f561d7a0da09bdbe79e653939a14d9f1614eb0565b604080516001600160a01b038316815242602082015243918101919091527f1a2dd071001ebf6e03174e3df5b305795a4ad5d41d8fdb9ba41dbbe23671342690606001612228565b6121eb6123a7612c39565b838361362c565b6123b6613313565b60008281526067602052604090206123ce8282615159565b507ff32b5bcd021361ac825fde09ff2121b0e5783f9f993196194fab9471799e54dd82826040516114c7929190615296565b600061240a612b9c565b6001600160a01b0316826001600160a01b0316149050919050565b612436612430612c39565b836130aa565b6124525760405162461bcd60e51b8152600401610a4190614e49565b610c2d848484846133bd565b612466613313565b81518051839160dd91612480918391602090910190614262565b5060208281015180516124999260018501920190614262565b50604082015180516124b59160028401916020909101906142ad565b5050815160e05550602081015160e15560408082015160e255606082015160e355608082015160e45560d9805460ff19166001179055517fc8e30ad432142bd0dfb602223be2c9714576cc124dbcee021719e2f29cdaf0b5906114c790849084906152af565b600080612526612b9c565b9050612531816134f0565b6000816001600160a01b031663fcad8c786040518163ffffffff1660e01b8152600401602060405180830381865afa158015612571573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125959190614fd3565b600086815260a2602052604080822054609954609a54925163b3e13ad160e01b8152600481018a9052602481019290925260448201526064810191909152919250906001600160a01b0383169063b3e13ad190608401602060405180830381865afa158015612608573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061262c9190615375565b600087815260a2602090815260409182902083905581518981529081018390529192507feb46efb0c7198679c79853a979eb80da18892986f9942ca8873ed7c4cefb5975910160405180910390a195945050505050565b60606065548211156126c05760405162461bcd60e51b815260206004820152600660248201526508595e1a5cdd60d21b6044820152606401610a41565b600082815260676020526040812080546126d990614df2565b80601f016020809104026020016040519081016040528092919081815260200182805461270590614df2565b80156127525780601f1061272757610100808354040283529160200191612752565b820191906000526020600020905b81548152906001019060200180831161273557829003601f168201915b5050505050905080516000146127685792915050565b6000612773846136fa565b805190915015612784579392505050565b61278c6134e1565b949350505050565b600061279e612b9c565b90506127a8613016565b6127c45760405162461bcd60e51b8152600401610a4190614e2c565b6127cd8361302e565b6127d68261302e565b609b5460008190036128185760405162461bcd60e51b815260206004820152600b60248201526a216475726162696c69747960a81b6044820152606401610a41565b6000848152609f602052604090205460ff1615801561284657506000838152609f602052604090205460ff16155b6128625760405162461bcd60e51b8152600401610a4190614f74565b816001600160a01b03166318d928316040518163ffffffff1660e01b8152600401602060405180830381865afa1580156128a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128c49190614f96565b156128e15760405162461bcd60e51b8152600401610a4190614fb3565b6128ea83613071565b7300379dd90b2a337c4652e286e4fbceadef940a2163806f099d61290c612b9c565b60975461010090046001600160a01b03166001612927612c39565b600a609854612936919061538e565b6040518663ffffffff1660e01b81526004016129569594939291906153b0565b60006040518083038186803b15801561296e57600080fd5b505af4158015612982573d6000803e3d6000fd5b505050600085815260a2602090815260409182902084905581518781529081018690527ffc4ee3fbea4df52f867d98cb796e03a5ae97157fa0c2372c6f61d4336543a7ee925001610c24565b60006129d8612b9c565b6001600160a01b031663fcad8c786040518163ffffffff1660e01b8152600401602060405180830381865afa158015612a15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a399190614fd3565b6001600160a01b03166351c9120d612a62609c6000868152602001908152602001600020612f87565b609b546040518363ffffffff1660e01b8152600401612a829291906153f9565b602060405180830381865afa158015612a9f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109419190615375565b6000816001600160a01b0316612ad7612b9c565b6001600160a01b0316635aa6e6756040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b14573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b389190614fd3565b6001600160a01b03161492915050565b6000826001600160a01b0316612b5d836114f5565b6001600160a01b0316149392505050565b6001600160a01b03918216600090815260066020908152604080832093909416825291909152205460ff1690565b6000610b9a610d3160017f5165972ef41194f06c5007493031d0b927c20741adcb74403b954009fd2c3618614eb0565b60006001600160e01b031982166380ac58cd60e01b1480612bfd57506001600160e01b03198216635b5e139f60e01b145b8061094157506301ffc9a760e01b6001600160e01b0319831614610941565b6000908152600360205260409020546001600160a01b0316151590565b6000612c44336114d3565b15612c56575060131936013560601c90565b503390565b600081815260056020526040902080546001600160a01b0319166001600160a01b0384169081179091558190612c90826114f5565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b612cd1612b9c565b604051633ec963c960e01b81523360048201526001600160a01b039190911690633ec963c990602401602060405180830381865afa158015612d17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d3b9190614f96565b612d725760405162461bcd60e51b815260206004820152600860248201526710b23ab733b2b7b760c11b6044820152606401610a41565b565b6000612d7e6137a9565b9050612d91612d8b612c39565b826137c2565b6000612d9b612b9c565b6001600160a01b031663fcad8c786040518163ffffffff1660e01b8152600401602060405180830381865afa158015612dd8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dfc9190614fd3565b6000838152609c602052604080822060b354915163fe1a90f160e01b815293945091927300379dd90b2a337c4652e286e4fbceadef940a219263fe1a90f192612e4d9287929160a3916004016154b7565b602060405180830381865af4158015612e6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e8e9190615375565b6000848152609d602052604090819020905163fe1a90f160e01b81529192507300379dd90b2a337c4652e286e4fbceadef940a219163fe1a90f191612ede9186919060a9906001906004016154b7565b602060405180830381865af4158015612efb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f1f9190615375565b506000838152609e60209081526040808320849055609b5460a290925290912055612f49836137dc565b60408051848152602081018390527f8a9dcf4e150b1153011b29fec302d5be0c13e84fa8f56ab78587f778a32a90dd910160405180910390a1505090565b60606000612f948361383e565b60408051602b808252610580820190925291925060009190602082016105608036833701905050905060005b8281101561300e57600080612fd58784613849565b915091506000811215612fe6575060005b80848381518110612ff957612ff96150e0565b60209081029190910101525050600101612fc0565b509392505050565b6000613021336114d3565b80610b9a57505032331490565b613036612c39565b6001600160a01b0316613048826114f5565b6001600160a01b03161461306e5760405162461bcd60e51b8152600401610a4190614ff0565b50565b61307a81613865565b6040518181527ff15a2816d9b33bd70f4b57d8cfbd7ee75d3231d8c52a030fef8a86fb4adfe94790602001612228565b60006130b582612c1c565b6131165760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b6064820152608401610a41565b6000613121836114f5565b9050806001600160a01b0316846001600160a01b0316148061315c5750836001600160a01b0316613151846109d9565b6001600160a01b0316145b8061278c575061278c8185612b6e565b826001600160a01b031661317f826114f5565b6001600160a01b0316146131e35760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201526437bbb732b960d91b6064820152608401610a41565b6001600160a01b0382166132455760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610a41565b61325083838361390c565b61325b600082612c5b565b6001600160a01b0383166000908152600460205260408120805460019290613284908490614eb0565b90915550506001600160a01b03821660009081526004602052604081208054600192906132b29084906150f6565b909155505060008181526003602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b61331b612b9c565b604051631430d62960e21b81523360048201526001600160a01b0391909116906350c358a490602401602060405180830381865afa158015613361573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133859190614f96565b612d725760405162461bcd60e51b815260206004820152600960248201526810b232b83637bcb2b960b91b6044820152606401610a41565b6133c884848461316c565b6133d484848484613946565b610c2d5760405162461bcd60e51b8152600401610a41906154ef565b600054610100900460ff1661340b5760005460ff161561340f565b303b155b6134495760405162461bcd60e51b815260206004820152600b60248201526a1a5b9a5d1a585b1a5e995960aa1b6044820152606401610a41565b600054610100900460ff16612d72576000805461ffff1916610101179055565b6134716135e6565b61347b8484613a4e565b61348482612277565b600160655560666134958282615159565b507f87cdeaffd8e70903d6ce7cc983fac3b09ca79e83818124c98e47a1d70f8027d681604051610c2491906143d7565b600054610100900460ff16612d72576000805461ff0019169055565b60606066805461095690614df2565b60405163ba79bf5f60e01b81523360048201526001600160a01b0382169063ba79bf5f90602401602060405180830381865afa158015613534573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135589190614f96565b806135ca5750336001600160a01b0316816001600160a01b031663524a562f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156135a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130489190614fd3565b61306e5760405162461bcd60e51b8152600401610a4190614ff0565b600054610100900460ff16612d725760405162461bcd60e51b815260206004820152600c60248201526b696e697469616c697a696e6760a01b6044820152606401610a41565b816001600160a01b0316836001600160a01b03160361368d5760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610a41565b6001600160a01b03838116600081815260066020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b6000818152609e6020908152604080832054835260a1909152902080546060919061372490614df2565b80601f016020809104026020016040519081016040528092919081815260200182805461375090614df2565b801561379d5780601f106137725761010080835404028352916020019161379d565b820191906000526020600020905b81548152906001019060200180831161378057829003601f168201915b50505050509050919050565b6065546000906137ba8160016150f6565b606555919050565b6121eb828260405180602001604052806000815250613a60565b600081815260da6020908152604080832060db909252909120613800919083613a93565b600091825260dc6020908152604092839020825181559082015160018201559181015160028301556060810151600383015560800151600490910155565b600061094182613dba565b60008080806138588686613dc5565b9097909650945050505050565b6000613870826114f5565b905061387e8160008461390c565b613889600083612c5b565b6001600160a01b03811660009081526004602052604081208054600192906138b2908490614eb0565b909155505060008281526003602052604080822080546001600160a01b0319169055518391906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b6000818152609f602052604090205460ff161561393b5760405162461bcd60e51b8152600401610a4190614f74565b610b83838383613df0565b60006001600160a01b0384163b15613a4357836001600160a01b031663150b7a0261396f612c39565b8786866040518563ffffffff1660e01b81526004016139919493929190615541565b6020604051808303816000875af19250505080156139cc575060408051601f3d908101601f191682019092526139c99181019061557e565b60015b613a29573d8080156139fa576040519150601f19603f3d011682016040523d82523d6000602084013e6139ff565b606091505b508051600003613a215760405162461bcd60e51b8152600401610a41906154ef565b805181602001fd5b6001600160e01b031916630a85bd0160e11b14905061278c565b506001949350505050565b613a566135e6565b6121eb8282613ea8565b613a6a8383613ec9565b613a776000848484613946565b610b835760405162461bcd60e51b8152600401610a41906154ef565b613ac56040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b6000613acf612b9c565b6001600160a01b031663fcad8c786040518163ffffffff1660e01b8152600401602060405180830381865afa158015613b0c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b309190614fd3565b9050600080826001600160a01b0316635cb8980f60dd6040518263ffffffff1660e01b8152600401613b62919061559b565b600060405180830381865afa158015613b7f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613ba79190810190615dc3565b91509150600080846001600160a01b0316635027f3848560b3546040518363ffffffff1660e01b8152600401613bde929190615e1c565b6000604051808303816000875af1158015613bfd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613c259190810190615e2f565b509150915060005b8151811015613c91576000828281518110613c4a57613c4a6150e0565b602002602001015190506000848381518110613c6857613c686150e0565b6020026020010151905081600014613c8757613c858c8284614008565b505b5050600101613c2d565b5060b354604051631409fce160e21b815260009182916001600160a01b03891691635027f38491613cc6918991600401615e1c565b6000604051808303816000875af1158015613ce5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613d0d9190810190615e2f565b509150915060005b8151811015613d79576000828281518110613d3257613d326150e0565b602002602001015190506000848381518110613d5057613d506150e0565b6020026020010151905081600014613d6f57613d6d8d8284614008565b505b5050600101613d15565b50506040805160a08101825260e054815260e154602082015260e2549181019190915260e354606082015260e45460808201529a9950505050505050505050565b600061094182614015565b60008080613dd3858561401f565b600081815260029690960160205260409095205494959350505050565b6001600160a01b038316613e4b57613e4681603580546000838152603660205260408120829055600182018355919091527fcfa4bec1d3298408bb5afcfcd9c430549c5b31f8aa5c5848151c0a55f473c34d0155565b613e6e565b816001600160a01b0316836001600160a01b031614613e6e57613e6e8382614032565b6001600160a01b038216613e8557610b83816140cf565b826001600160a01b0316826001600160a01b031614610b8357610b83828261417e565b613eb06135e6565b6001613ebc8382615159565b506002610b838282615159565b6001600160a01b038216613f1f5760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610a41565b613f2881612c1c565b15613f755760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610a41565b613f816000838361390c565b6001600160a01b0382166000908152600460205260408120805460019290613faa9084906150f6565b909155505060008181526003602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b600061278c8484846141c2565b6000610941825490565b600061402b83836141df565b9392505050565b6000600161403f8461162f565b6140499190614eb0565b60008381526034602052604090205490915080821461409c576001600160a01b03841660009081526033602090815260408083208584528252808320548484528184208190558352603490915290208190555b5060009182526034602090815260408084208490556001600160a01b039094168352603381528383209183525290812055565b6035546000906140e190600190614eb0565b60008381526036602052604081205460358054939450909284908110614109576141096150e0565b90600052602060002001549050806035838154811061412a5761412a6150e0565b600091825260208083209091019290925582815260369091526040808220849055858252812055603580548061416257614162615eaa565b6001900381819060005260206000200160009055905550505050565b60006141898361162f565b6001600160a01b039093166000908152603360209081526040808320868452825280832085905593825260349052919091209190915550565b6000828152600284016020526040812082905561278c8484614209565b60008260000182815481106141f6576141f66150e0565b9060005260206000200154905092915050565b600081815260018301602052604081205461402b9084908490849061425a57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610941565b506000610941565b82805482825590600052602060002090810192821561429d579160200282015b8281111561429d578251825591602001919060010190614282565b506142a9929150614349565b5090565b82805482825590600052602060002090601f0160209004810192821561429d5791602002820160005b8382111561431357835183826101000a81548160ff02191690831515021790555092602001926001016020816000010492830192600103026142d6565b80156143405782816101000a81549060ff0219169055600101602081600001049283019260010302614313565b50506142a99291505b5b808211156142a9576000815560010161434a565b6001600160e01b03198116811461306e57600080fd5b60006020828403121561438657600080fd5b813561402b8161435e565b6000815180845260005b818110156143b75760208185018101518683018201520161439b565b506000602082860101526020601f19601f83011685010191505092915050565b60208152600061402b6020830184614391565b6000602082840312156143fc57600080fd5b5035919050565b6001600160a01b038116811461306e57600080fd5b803561442381614403565b919050565b6000806040838503121561443b57600080fd5b823561444681614403565b946020939093013593505050565b60008151808452602080850194506020840160005b8381101561448557815187529582019590820190600101614469565b509495945050505050565b60208152600061402b6020830184614454565b600080604083850312156144b657600080fd5b50508035926020909101359150565b6000806000606084860312156144da57600080fd5b83356144e581614403565b925060208401356144f581614403565b929592945050506040919091013590565b634e487b7160e01b600052602160045260246000fd5b602081016005831061453057614530614506565b91905290565b634e487b7160e01b600052604160045260246000fd5b60405160c081016001600160401b038111828210171561456e5761456e614536565b60405290565b604051606081016001600160401b038111828210171561456e5761456e614536565b604051601f8201601f191681016001600160401b03811182821017156145be576145be614536565b604052919050565b60006001600160401b038211156145df576145df614536565b5060051b60200190565b600082601f8301126145fa57600080fd5b8135602061460f61460a836145c6565b614596565b8083825260208201915060208460051b87010193508684111561463157600080fd5b602086015b8481101561464d5780358352918301918301614636565b509695505050505050565b600060c0828403121561466a57600080fd5b61467261454c565b905081356001600160401b038082111561468b57600080fd5b614697858386016145e9565b835260208401359150808211156146ad57600080fd5b6146b9858386016145e9565b602084015260408401359150808211156146d257600080fd5b6146de858386016145e9565b604084015260608401359150808211156146f757600080fd5b50614704848285016145e9565b6060830152506080820135608082015260a082013560a082015292915050565b600080600080600080600060e0888a03121561473f57600080fd5b87356001600160401b038082111561475657600080fd5b6147628b838c01614658565b985060208a013591508082111561477857600080fd5b506147858a828b01614658565b979a9799505050506040860135956060810135956080820135955060a0820135945060c09091013592509050565b600080600080608085870312156147c957600080fd5b8435935060208501356147db81614403565b93969395505050506040820135916060013590565b60006020828403121561480257600080fd5b813561402b81614403565b8151815260208083015190820152604080830151908201526060808301519082015260808101610941565b60006001600160401b0383111561485157614851614536565b614864601f8401601f1916602001614596565b905082815283838301111561487857600080fd5b828260208301376000602084830101529392505050565b600082601f8301126148a057600080fd5b61402b83833560208501614838565b8035600c811061442357600080fd5b60008060008060008060008060006101208a8c0312156148dd57600080fd5b6148e68a614418565b985060208a01356001600160401b038082111561490257600080fd5b61490e8d838e0161488f565b995060408c013591508082111561492457600080fd5b6149308d838e0161488f565b985060608c013591508082111561494657600080fd5b506149538c828d0161488f565b96505061496260808b01614418565b945060a08a0135935060c08a0135925061497e60e08b016148af565b91506101008a013590509295985092959850929598565b600080604083850312156149a857600080fd5b82356001600160401b038111156149be57600080fd5b6149ca8582860161488f565b95602094909401359450505050565b60e0815260006149ec60e0830186614454565b82810360208401526149fe8186614454565b91505061278c604083018480518252602081015160208301526040810151604083015260608101516060830152608081015160808301525050565b801515811461306e57600080fd5b60008060008060008060c08789031215614a6057600080fd5b863595506020870135614a7281614403565b945060408701359350606087013592506080870135614a9081614403565b915060a0870135614aa081614a39565b809150509295509295509295565b600080600060608486031215614ac357600080fd5b8335925060208401356144f581614403565b600060208284031215614ae757600080fd5b81356001600160401b03811115614afd57600080fd5b61278c8482850161488f565b60008060408385031215614b1c57600080fd5b8235614b2781614403565b91506020830135614b3781614a39565b809150509250929050565b60008060408385031215614b5557600080fd5b823591506020830135614b3781614403565b60008060408385031215614b7a57600080fd5b8235915060208301356001600160401b03811115614b9757600080fd5b614ba38582860161488f565b9150509250929050565b60008060008060808587031215614bc357600080fd5b8435614bce81614403565b93506020850135614bde81614403565b92506040850135915060608501356001600160401b03811115614c0057600080fd5b8501601f81018713614c1157600080fd5b614c2087823560208401614838565b91505092959194509250565b600060a08284031215614c3e57600080fd5b60405160a081018181106001600160401b0382111715614c6057614c60614536565b806040525080915082358152602083013560208201526040830135604082015260608301356060820152608083013560808201525092915050565b60008060c08385031215614cae57600080fd5b82356001600160401b0380821115614cc557600080fd5b9084019060608287031215614cd957600080fd5b614ce1614574565b823582811115614cf057600080fd5b614cfc888286016145e9565b82525060208084013583811115614d1257600080fd5b614d1e898287016145e9565b8284015250604084013583811115614d3557600080fd5b80850194505087601f850112614d4a57600080fd5b83359250614d5a61460a846145c6565b83815260059390931b84018101928181019089851115614d7957600080fd5b948201945b84861015614da0578535614d9181614a39565b82529482019490820190614d7e565b8060408501525050819550614db788828901614c2c565b9450505050509250929050565b60008060408385031215614dd757600080fd5b8235614de281614403565b91506020830135614b3781614403565b600181811c90821680614e0657607f821691505b602082108103614e2657634e487b7160e01b600052602260045260246000fd5b50919050565b602080825260039082015262454f4160e81b604082015260600190565b60208082526031908201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6040820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b8181038181111561094157610941614e9a565b6000815160c08452614ed860c0850182614454565b905060208301518482036020860152614ef18282614454565b91505060408301518482036040860152614f0b8282614454565b91505060608301518482036060860152614f258282614454565b9150506080830151608085015260a083015160a08501528091505092915050565b604081526000614f596040830185614ec3565b8281036020840152614f6b8185614ec3565b95945050505050565b602080825260089082015267195c5d5a5c1c195960c21b604082015260600190565b600060208284031215614fa857600080fd5b815161402b81614a39565b6020808252600690820152651c185d5cd95960d21b604082015260600190565b600060208284031215614fe557600080fd5b815161402b81614403565b6020808252600990820152683337b93134b23232b760b91b604082015260600190565b8381526020810183905281516001600160a01b031660408201526101e0810160208301516001600160a01b0381166060840152506040830151608083015260608301516001600160a01b03811660a084015250608083015160c083015260a083015160e083015260c0830151610100818185015260e0850151915061012082818601528186015161014086015280860151925050506150d7610160840182805182526020810151602083015260408101516040830152606081015160608301525050565b50949350505050565b634e487b7160e01b600052603260045260246000fd5b8082018082111561094157610941614e9a565b601f821115610b83576000816000526020600020601f850160051c810160208610156151325750805b601f850160051c820191505b818110156151515782815560010161513e565b505050505050565b81516001600160401b0381111561517257615172614536565b615186816151808454614df2565b84615109565b602080601f8311600181146151bb57600084156151a35750858301515b600019600386901b1c1916600185901b178555615151565b600085815260208120601f198616915b828110156151ea578886015182559484019460019091019084016151cb565b50858210156152085787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60408152600061522b6040830185614391565b90508260208301529392505050565b6020815260018060a01b038251166020820152602082015160408201526000604083015160a0606084015261527260c0840182614454565b90506060840151151560808401526080840151151560a08401528091505092915050565b82815260406020820152600061278c6040830184614391565b60c0815260008351606060c08401526152cc610120840182614454565b905060208086015160bf19808685030160e08701526152eb8483614454565b604089015187820390920161010088015281518082529184019450600092508301905b808310156153305784511515825293830193600192909201919083019061530e565b50935061536c90508482018680518252602081015160208301526040810151604083015260608101516060830152608081015160808301525050565b50509392505050565b60006020828403121561538757600080fd5b5051919050565b6000826153ab57634e487b7160e01b600052601260045260246000fd5b500490565b6001600160a01b038681168252858116602083015260a0820190600386106153da576153da614506565b8560408401528085166060840152508260808301529695505050505050565b60408152600061522b6040830185614454565b600081548084526020808501945083600052602060002060005b8381101561448557815487529582019560019182019101615426565b60c08252600061545560c084018361540c565b838103602085015261546a816001850161540c565b90508381036040850152615481816002850161540c565b90508381036060850152615498816003850161540c565b6004840154608086015260059093015460a09094019390935250919050565b60018060a01b03851681528360208201526080604082015260006154de6080830185615442565b905082606083015295945050505050565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b6001600160a01b038581168252841660208201526040810183905260806060820181905260009061557490830184614391565b9695505050505050565b60006020828403121561559057600080fd5b815161402b8161435e565b60006020808352606080828501526155b6608085018661540c565b6001601f196040818885030160408901526155d384848b0161540c565b888103929092016060890152600289018054808452600091825260208083209650909301925b80601f8301101561585c57855460ff808216151586526156218a8701828460081c1615159052565b615633858701828460101c1615159052565b615645898701828460181c1615159052565b818a1c81161515608087015260a0615665818801838560281c1615159052565b60c0615679818901848660301c1615159052565b60e061568d818a01858760381c1615159052565b84881c841615156101008a01526156ae6101208a01858760481c1615159052565b6156c26101408a01858760501c1615159052565b6156d66101608a01858760581c1615159052565b848c1c841615156101808a01526156f76101a08a01858760681c1615159052565b61570b6101c08a01858760701c1615159052565b61571f6101e08a01858760781c1615159052565b6157336102008a01858760801c1615159052565b6157476102208a01858760881c1615159052565b61575b6102408a01858760901c1615159052565b61576f6102608a01858760981c1615159052565b84831c841615156102808a01526157906102a08a01858760a81c1615159052565b6157a46102c08a01858760b01c1615159052565b6157b86102e08a01858760b81c1615159052565b84821c841615156103008a01526157d96103208a01858760c81c1615159052565b6157ed6103408a01858760d01c1615159052565b6158016103608a01858760d81c1615159052565b84901c83161515610380890152505060e882901c811615156103a08701526158336103c08701828460f01c1615159052565b506158466103e086018260f81c15159052565b50948401946104009390930192908701906155f9565b85549650808210156158795760ff87161515845292870192908401905b8082101561589b576158928460ff8960081c1615159052565b92870192908401905b808210156158bd576158b48460ff8960101c1615159052565b92870192908401905b808210156158df576158d68460ff8960181c1615159052565b92870192908401905b808210156158fa5786881c60ff161515845292870192908401905b8082101561591c576159138460ff8960281c1615159052565b92870192908401905b8082101561593e576159358460ff8960301c1615159052565b92870192908401905b80821015615960576159578460ff8960381c1615159052565b92870192908401905b80821015615982576159798460ff8960401c1615159052565b92870192908401905b808210156159a45761599b8460ff8960481c1615159052565b92870192908401905b808210156159c6576159bd8460ff8960501c1615159052565b92870192908401905b808210156159e8576159df8460ff8960581c1615159052565b92870192908401905b80821015615a0a57615a018460ff8960601c1615159052565b92870192908401905b80821015615a2c57615a238460ff8960681c1615159052565b92870192908401905b80821015615a4e57615a458460ff8960701c1615159052565b92870192908401905b80821015615a7057615a678460ff8960781c1615159052565b92870192908401905b80821015615a9257615a898460ff8960801c1615159052565b92870192908401905b80821015615ab457615aab8460ff8960881c1615159052565b92870192908401905b80821015615ad657615acd8460ff8960901c1615159052565b92870192908401905b80821015615af857615aef8460ff8960981c1615159052565b92870192908401905b80821015615b1a57615b118460ff8960a01c1615159052565b92870192908401905b80821015615b3c57615b338460ff8960a81c1615159052565b92870192908401905b80821015615b5e57615b558460ff8960b01c1615159052565b92870192908401905b80821015615b8057615b778460ff8960b81c1615159052565b92870192908401905b80821015615ba257615b998460ff8960c01c1615159052565b92870192908401905b80821015615bc457615bbb8460ff8960c81c1615159052565b92870192908401905b80821015615be657615bdd8460ff8960d01c1615159052565b92870192908401905b80821015615c0857615bff8460ff8960d81c1615159052565b92870192908401905b80821015615c2a57615c218460ff8960e01c1615159052565b92870192908401905b80821015615c4c57615c438460ff8960e81c1615159052565b92870192908401905b80821015615c6e57615c658460ff8960f01c1615159052565b92870192908401905b80821015615c8957615c84848860f81c15159052565b928701925b50919998505050505050505050565b600082601f830112615ca957600080fd5b81516020615cb961460a836145c6565b8083825260208201915060208460051b870101935086841115615cdb57600080fd5b602086015b8481101561464d5780518352918301918301615ce0565b600060c08284031215615d0957600080fd5b615d1161454c565b905081516001600160401b0380821115615d2a57600080fd5b615d3685838601615c98565b83526020840151915080821115615d4c57600080fd5b615d5885838601615c98565b60208401526040840151915080821115615d7157600080fd5b615d7d85838601615c98565b60408401526060840151915080821115615d9657600080fd5b50615da384828501615c98565b6060830152506080820151608082015260a082015160a082015292915050565b60008060408385031215615dd657600080fd5b82516001600160401b0380821115615ded57600080fd5b615df986838701615cf7565b93506020850151915080821115615e0f57600080fd5b50614ba385828601615cf7565b60408152600061522b6040830185614ec3565b600080600060608486031215615e4457600080fd5b83516001600160401b0380821115615e5b57600080fd5b615e6787838801615c98565b94506020860151915080821115615e7d57600080fd5b50615e8a86828701615c98565b925050604084015160068110615e9f57600080fd5b809150509250925092565b634e487b7160e01b600052603160045260246000fdfea2646970667358221220c7637622787df9ce503483ee2d3ac40d79b6b54d50f45c00a7de8df04c48540164736f6c63430008170033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
0x099C314F792e1F91f53765Fc64AaDCcf4dCf1538
Loading...
Loading
Loading...
Loading

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.