Contract

0x9C4d20387b553F27806514aD99eB47785C69812D

Overview

S Balance

Sonic LogoSonic LogoSonic Logo0 S

S Value

-

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

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

Contract Source Code Verified (Exact Match)

Contract Name:
ItemBase

Compiler Version
v0.8.23+commit.f704f362

Optimization Enabled:
Yes with 50 runs

Other Settings:
istanbul EvmVersion
File 1 of 33 : ItemBase.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;

import "./NftBase.sol";
import "../interfaces/IItem.sol";
import "../interfaces/IItemController.sol";

/// @title ItemBase implementation.
///        All game logic should be placed in dedicated controllers.
contract ItemBase is NftBase, IItem {

  //region ------------------------ Data types and constants

  /// @custom:storage-location erc7201:item.base.storage
  struct ItemBaseStorage {
    mapping(uint8 => string) itemUriByRarity;
  }

  /// @notice Version of the contract
  string public constant VERSION = "2.0.0";
  // keccak256(abi.encode(uint256(keccak256("item.base.storage")) - 1)) & ~bytes32(uint256(0xff))
  bytes32 private constant ItemBaseStorageLocation = 0x2dd41482e37d186fdf6545c563673785f2bcb485d6039f6c172d58b496a6e000;
  //endregion ------------------------ Data types and constants

  //region ------------------------ Initializer

  function init(
    address controller_,
    string memory name_,
    string memory symbol_,
    string memory uri_
  ) external initializer {
    __NftBase_init(name_, symbol_, controller_, uri_);
  }
  //endregion ------------------------ Initializer

  //region ------------------------ Restrictions

  function onlyItemController(address itemController, address sender) internal pure {
    if (itemController != sender) revert IAppErrors.ErrorNotItemController(sender);
  }

  function _beforeTokenTransfer(uint tokenId) internal view override {
    if (
      !IItemController(IController(controller()).itemController()).isAllowedToTransfer(address(this), tokenId)
    ) revert IAppErrors.EquippedItemIsNotAllowedToTransfer(tokenId);
  }
  //endregion ------------------------ Restrictions

  //region ------------------------ Views

  function _getItemBaseStorage() private pure returns (ItemBaseStorage storage $) {
    assembly {
      $.slot := ItemBaseStorageLocation
    }
    return $;
  }

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

  function _specificURI(uint tokenId) internal view override returns (string memory) {
    IItemController ic = IItemController(IController(controller()).itemController());
    return _getItemBaseStorage().itemUriByRarity[uint8(ic.itemInfo(address(this), tokenId).rarity)];
  }
  //endregion ------------------------ Views

  //region ------------------------ Gov actions

  function setItemUriByRarity(string memory uri, uint8 rarity) external {
    onlyDeployer();

    _getItemBaseStorage().itemUriByRarity[rarity] = uri;
    emit IApplicationEvents.UriByRarityChanged(uri, rarity);
  }
  //endregion ------------------------ Gov actions

  //region ------------------------ Actions

  function mintFor(address recipient) external override returns (uint tokenId) {
    onlyItemController(IController(controller()).itemController(), msg.sender);

    tokenId = _incrementAndGetId();
    _safeMint(recipient, tokenId);

    emit IApplicationEvents.ItemMinted(tokenId);
  }

  /// @dev Some stories can destroy items
  function burn(uint tokenId) external override {
    onlyItemController(IController(controller()).itemController(), msg.sender);

    _burn(tokenId);

    emit IApplicationEvents.ItemBurned(tokenId);
  }

  /// @dev Controller can transfer item from one address to another.
  ///      It must be performed only with properly check requirements.
  function controlledTransfer(address from, address to, uint tokenId) external override {
    onlyItemController(IController(controller()).itemController(), msg.sender);

    _safeTransfer(from, to, tokenId);
  }
  //endregion ------------------------ Actions

}

File 2 of 33 : IAppErrors.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;

/// @notice All errors of the app
interface IAppErrors {

  //region ERC20Errors
  /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
  error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

  /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
  error ERC20InvalidSender(address sender);

  /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
  error ERC20InvalidReceiver(address receiver);

  /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
  error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

  /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
  error ERC20InvalidApprover(address approver);

  /**
   * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
  error ERC20InvalidSpender(address spender);

  //endregion ERC20Errors

  //region ERC721Errors

  /**
  * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
  error ERC721InvalidOwner(address owner);

  /**
   * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
  error ERC721NonexistentToken(uint256 tokenId);

  /**
   * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
  error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

  /**
   * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
  error ERC721InvalidSender(address sender);

  /**
   * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
  error ERC721InvalidReceiver(address receiver);

  /**
   * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
  error ERC721InsufficientApproval(address operator, uint256 tokenId);

  /**
   * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
  error ERC721InvalidApprover(address approver);

  /**
   * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
  error ERC721InvalidOperator(address operator);

  //endregion ERC721Errors

  error ZeroAddress();
  error ZeroValueNotAllowed();
  error ZeroToken();
  error LengthsMismatch();
  error NotEnoughBalance();
  error NotEnoughAllowance();
  error EmptyNameNotAllowed();
  error NotInitialized();
  error AlreadyInitialized();
  error ReentrancyGuardReentrantCall();
  error TooLongString();
  error AlreadyDeployed(address deployed);

  //region Restrictions
  error ErrorNotDeployer(address sender);
  error ErrorNotGoc();
  error NotGovernance(address sender);
  error ErrorOnlyEoa();
  error NotEOA(address sender);
  error ErrorForbidden(address sender);
  error AdminOnly();
  error ErrorNotItemController(address sender);
  error ErrorNotHeroController(address sender);
  error ErrorNotDungeonFactory(address sender);
  error ErrorNotObjectController(address sender);
  error ErrorNotStoryController();
  error ErrorNotAllowedSender();
  error MintNotAllowed();
  //endregion Restrictions

  //region PackingLib
  error TooHighValue(uint value);
  error IntValueOutOfRange(int value);
  error OutOfBounds(uint index, uint length);
  error UnexpectedValue(uint expected, uint actual);
  error WrongValue(uint newValue, uint actual);
  error IntOutOfRange(int value);
  error ZeroValue();
  /// @notice packCustomDataChange requires an input string with two zero bytes at the beginning
  ///         0xXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX0000
  /// This error happens if these bytes are not zero
  error IncompatibleInputString();
  error IncorrectOtherItemTypeKind(uint8 kind);
  //endregion PackingLib

  //region Hero
  error ErrorHeroIsNotRegistered(address heroToken);
  error ErrorHeroIsDead(address heroToken, uint heroTokenId);
  error ErrorHeroNotInDungeon();
  error HeroInDungeon();
  error ErrorNotOwner(address token, uint tokenId);
  error Staked(address heroToken, uint heroId);
  error NameTaken();
  error TooBigName();
  error WrongSymbolsInTheName();
  error NoPayToken(address token, uint payTokenAmount);
  error AlreadyHaveReinforcement();
  /// @notice SIP-001 - Reinforcement requires 3 skills
  error ErrorReinforcementRequiresThreeSkills();
  error WrongTier(uint tier);
  error NotEnoughNgLevel(uint8 ngLevel);
  error NgpNotActive(address hero);
  error RebornNotAllowed();
  error AlreadyPrePaidHero();
  //endregion Hero

  //region Dungeon
  error ErrorDungeonIsFreeAlready();
  error ErrorNoEligibleDungeons();
  error ErrorDungeonBusy();
  error ErrorNoDungeonsForBiome(uint8 heroBiome);
  error ErrorDungeonCompleted();
  error ErrorAlreadyInDungeon();
  error NotEnoughTokens(uint balance, uint expectedBalance);
  error DungeonAlreadySpecific(uint16 dungNum);
  error DungeonAlreadySpecific2(uint16 dungNum);
  error WrongSpecificDungeon();
  //endregion Dungeon

  //region Items
  error ErrorItemNotEligibleForTheSlot(uint itemType, uint8 itemSlot);
  error ErrorItemSlotBusyHand(uint8 slot);
  error ErrorItemSlotBusy();
  error ErrorItemNotInSlot();
  error ErrorConsumableItemIsUsed(address item);
  error ErrorCannotRemoveItemFromMap();
  error ErrorCannotRemoveDataFromMap();
  error EquippedItemsExist();
  error ItemEquipped(address item, uint itemId);
  error ZeroItemMetaType();
  error NotZeroOtherItemMetaType();
  error ZeroLevel();
  error ItemTypeChanged();
  error ItemMetaTypeChanged();
  error UnknownItem(address item);
  error ErrorEquipForbidden();
  error EquipForbiddenInDungeon();
  error TakeOffForbiddenInDungeon();
  error Consumable(address item);
  error NotConsumable(address item);
  error Broken(address item);
  error ZeroLife();
  error RequirementsToItemAttributes();
  error NotEquipped(address item);
  error ZeroDurability();
  error ZeroAugmentation();
  error TooHighAgLevel(uint8 augmentationLevel);
  error UseForbiddenZeroPayToken();
  error IncorrectMinMaxAttributeRange(int32 min, int32 max);
  error SameIdsNotAllowed();
  error ZeroFragility();
  error OtherTypeItemNotRepairable();
  error NotOther();
  error DoubleItemUsageForbidden(uint itemIndex, address[] items);
  error ItemAlreadyUsedInSlot(address item, uint8 equippedSlot);
  error WrongWayToRegisterItem();
  error UnionItemNotFound(address item);
  error WrongListUnionItemTokens(address item, uint countTokens, uint requiredCountTokens);
  error UnknownUnionConfig(uint unionConfigId);
  error UserHasNoKeyPass(address user, address keyPassItem);
  error MaxValue(uint value);
  error UnexpectedOtherItem(address item);
  error NotExist();
  //endregion Items

  //region Stages
  error ErrorWrongStage(uint stage);
  error ErrorNotStages();
  //endregion Stages

  //region Level
  error ErrorWrongLevel(uint heroLevel);
  error ErrorLevelTooLow(uint heroLevel);
  error ErrorHeroLevelStartFrom1();
  error ErrorWrongLevelUpSum();
  error ErrorMaxLevel();
  //endregion Level

  //region Treasure
  error ErrorNotValidTreasureToken(address treasureToken);
  //endregion Treasure

  //region State
  error ErrorPaused();
  error ErrorNotReady();
  error ErrorNotObject1();
  error ErrorNotObject2();
  error ErrorNotCompleted();
  //endregion State

  //region Biome
  error ErrorNotBiome();
  error ErrorIncorrectBiome(uint biome);
  error TooHighBiome(uint biome);
  //endregion Biome

  //region Misc
  error ErrorWrongMultiplier(uint multiplier);
  error ErrorNotEnoughMana(uint32 mana, uint requiredMana);
  error ErrorExperienceMustNotDecrease();
  error ErrorNotEnoughExperience();
  error ErrorNotChances();
  error ErrorNotEligible(address heroToken, uint16 dungNum);
  error ErrorZeroKarmaNotAllowed();
  //endregion Misc

  //region GOC
  error GenObjectIdBiomeOverflow(uint8 biome);
  error GenObjectIdSubTypeOverflow(uint subType);
  error GenObjectIdIdOverflow(uint id);
  error UnknownObjectTypeGoc1(uint8 objectType);
  error UnknownObjectTypeGoc2(uint8 objectType);
  error UnknownObjectTypeGocLib1(uint8 objectType);
  error UnknownObjectTypeGocLib2(uint8 objectType);
  error UnknownObjectTypeForSubtype(uint8 objectSubType);
  error FightDelay();
  error ZeroChance();
  error TooHighChance(uint32 chance);
  error TooHighRandom(uint random);
  error EmptyObjects();
  error ObjectNotFound();
  error WrongGetObjectTypeInput();
  error WrongChances(uint32 chances, uint32 maxChances);
  //endregion GOC

  //region Story
  error PageNotRemovedError(uint pageId);
  error NotItem1();
  error NotItem2();
  error NotRandom(uint32 random);
  error NotHeroData();
  error NotGlobalData();
  error ZeroStoryIdRemoveStory();
  error ZeroStoryIdStoryAction();
  error ZeroStoryIdAction();
  error NotEnoughAmount(uint balance, uint requiredAmount);
  error NotAnswer();
  error AnswerStoryIdMismatch(uint16 storyId, uint16 storyIdFromAnswerHash);
  error AnswerPageIdMismatch(uint16 pageId, uint16 pageIdFromAnswerHash);
  //endregion Story

  //region FightLib
  error NotMagic();
  error NotAType(uint atype);
  //endregion FightLib

  //region MonsterLib
  error NotYourDebuffItem();
  error UnknownAttackType(uint attackType);
  error NotYourAttackItem();
  /// @notice The skill item cannot be used because it doesn't belong either to the hero or to the hero's helper
  error NotYourBuffItem();
  //endregion MonsterLib

  //region GameToken
  error ApproveToZeroAddress();
  error MintToZeroAddress();
  error TransferToZeroAddress();
  error TransferAmountExceedsBalance(uint balance, uint value);
  error InsufficientAllowance();
  error BurnAmountExceedsBalance();
  error NotMinter(address sender);
  //endregion GameToken

  //region NFT
  error TokenTransferNotAllowed();
  error IdOverflow(uint id);
  error NotExistToken(uint tokenId);
  error EquippedItemIsNotAllowedToTransfer(uint tokenId);
  //endregion NFT

  //region CalcLib
  error TooLowX(uint x);
  //endregion CalcLib

  //region Controller
  error NotFutureGovernance(address sender);
  //endregion Controller

  //region Oracle
  error OracleWrongInput();
  //endregion Oracle

  //region ReinforcementController
  error AlreadyStaked();
  error MaxFee(uint8 fee);
  error MinFee(uint8 fee);
  error StakeHeroNotStats();
  error NotStaked();
  error NoStakedHeroes();
  error GuildHelperNotAvailable(uint guildId, address helper, uint helperId);
  error HelperNotAvailableInGivenBiome();
  //endregion ReinforcementController

  //region SponsoredHero
  error InvalidHeroClass();
  error ZeroAmount();
  error InvalidProof();
  error NoHeroesAvailable();
  error AlreadyRegistered();
  //endregion SponsoredHero

  //region SacraRelay
  error SacraRelayNotOwner();
  error SacraRelayNotDelegator();
  error SacraRelayNotOperator();
  error SacraRelayInvalidChainId(uint callChainId, uint blockChainId);
  error SacraRelayInvalidNonce(uint callNonce, uint txNonce);
  error SacraRelayDeadline();
  error SacraRelayDelegationExpired();
  error SacraRelayNotAllowed();
  error SacraRelayInvalidSignature();
  /// @notice This error is generated when custom error is caught
  /// There is no info about custom error in SacraRelay
  /// but you can decode custom error by selector, see tests
  error SacraRelayNoErrorSelector(bytes4 selector, string tracingInfo);
  /// @notice This error is generated when custom error is caught
  /// There is no info about custom error in SacraRelay
  /// but you can decode custom error manually from {errorBytes} as following:
  /// if (keccak256(abi.encodeWithSignature("MyError()")) == keccak256(errorBytes)) { ... }
  error SacraRelayUnexpectedReturnData(bytes errorBytes, string tracingInfo);
  error SacraRelayCallToNotContract(address notContract, string tracingInfo);
  //endregion SacraRelay

  //region Misc
  error UnknownHeroClass(uint heroClass);
  error AbsDiff(int32 a, int32 b);
  //region Misc

  //region ------------------------ UserController
  error NoAvailableLootBox(address msgSender, uint lootBoxKind);
  error FameHallHeroAlreadyRegistered(uint8 openedNgLevel);

  //endregion ------------------------ UserController

  //region ------------------------ Guilds
  error AlreadyGuildMember();
  error NotGuildMember();
  error WrongGuild();
  error GuildActionForbidden(uint right);
  error GuildHasMaxSize(uint guildSize);
  error GuildHasMaxLevel(uint level);
  error TooLongUrl();
  error TooLongDescription();
  error CannotRemoveGuildOwnerFromNotEmptyGuild();
  error GuildControllerOnly();
  error GuildAlreadyHasShelter();
  error ShelterIsBusy();
  error ShelterIsNotRegistered();
  error ShelterIsNotOwnedByTheGuild();
  error ShelterIsInUse();
  error GuildHasNoShelter();
  error ShelterBidIsNotAllowedToBeUsed();
  error ShelterHasHeroesInside();
  error SecondGuildAdminIsNotAllowed();
  error NotEnoughGuildBankBalance(uint guildId);

  error GuildReinforcementCooldownPeriod();
  error NoStakedGuildHeroes();
  error NotStakedInGuild();
  error ShelterHasNotEnoughLevelForReinforcement();
  error NotBusyGuildHelper();

  error GuildRequestNotActive();
  error GuildRequestNotAvailable();
  error NotAdminCannotAddMemberWithNotZeroRights();
  //endregion ------------------------ Guilds

  //region ------------------------ Shelters
  error ErrorNotShelterController();
  error ErrorNotGuildController();
  error ShelterHasNotItem(uint shelterId, address item);
  error MaxNumberItemsSoldToday(uint numSoldItems, uint limit);
  error GuildHasNotEnoughPvpPoints(uint64 pointsAvailable, uint pointRequired);
  error FreeShelterItemsAreNotAllowed(uint shelterId, address item);
  error TooLowShelterLevel(uint8 shelterLevel, uint8 allowedShelterLevel);
  error NotEnoughPvpPointsCapacity(address user, uint usedPoints, uint pricePvpPoints, uint64 capactiy);
  error IncorrectShelterLevel(uint8 shelterLevel);
  //endregion ------------------------ Shelters

  //region ------------------------ Auction
  error WrongAuctionPosition();
  error AuctionPositionClosed();
  error AuctionBidOpened(uint positionId);
  error TooLowAmountToBid();
  error AuctionEnded();
  error TooLowAmountForNewBid();
  error AuctionSellerOnly();
  error AuctionBuyerOnly();
  error AuctionBidNotFound();
  error AuctionBidClosed();
  error OnlyShelterAuction();
  error CannotCloseLastBid();
  error AuctionNotEnded();
  error NotShelterAuction();
  error AuctionPositionOpened(uint positionId);
  error AuctionSellerCannotBid();
  error AuctionGuildWithShelterCannotBid();
  error AuctionBidExists();
  //endregion ------------------------ Auction

  //region ------------------------ Pawnshop
  error AuctionPositionNotSupported(uint positionId);
  error PositionNotSupported(uint positionId);
  error NotNftPositionNotSupported(uint positionId);
  error CallFailed(bytes callResultData);

  error PawnShopZeroOwner();
  error PawnShopZeroFeeRecipient();
  error PawnShopNotOwner();
  error PawnShopAlreadyAnnounced();
  error PawnShopTimeLock();
  error PawnShopWrongAddressValue();
  error PawnShopWrongUintValue();
  error PawnShopZeroAddress();
  error PawnShopTooHighValue();
  error PawnShopZeroAToken();
  error PawnShopZeroCToken();
  error PawnShopWrongAmounts();
  error PawnShopPosFeeForInstantDealForbidden();
  error PawnShopPosFeeAbsurdlyHigh();
  error PawnShopIncorrect();
  error PawnShopWrongId();
  error PawnShopNotBorrower();
  error PawnShopPositionClosed();
  error PawnShopPositionExecuted();
  error PawnShopWrongBidAmount();
  error PawnShopTooLowBid();
  error PawnShopNewBidTooLow();
  error PawnShopBidAlreadyExists();
  error PawnShopAuctionEnded();
  error PawnShopNotLender();
  error PawnShopTooEarlyToClaim();
  error PawnShopPositionNotExecuted();
  error PawnShopAlreadyClaimed();
  error PawnShopAuctionNotEnded();
  error PawnShopBidClosed();
  error PawnShopNoBids();
  error PawnShopAuctionBidNotFound();
  error PawnShopWrongBid();
  error PawnShopBidNotFound();

  //endregion ------------------------ Pawnshop
}

File 3 of 33 : IApplicationEvents.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;

import "./IGOC.sol";
import "./IStatController.sol";
import "./IDungeonFactory.sol";
import "./IStoryController.sol";
import "./IFightCalculator.sol";

/// @notice All events of the app
interface IApplicationEvents {

  //region ------------------ StatController
  event HeroItemSlotChanged(
    address heroToken,
    uint heroTokenId,
    uint itemType,
    uint itemSlot,
    address itemToken,
    uint itemTokenId,
    bool equip,
    address caller
  );
  event CurrentStatsChanged(
    address heroToken,
    uint heroTokenId,
    IStatController.ChangeableStats change,
    bool increase,
    address caller
  );
  event BonusAttributesChanged(
    address heroToken,
    uint heroTokenId,
    bool add,
    bool temporally,
    address caller
  );
  event TemporallyAttributesCleared(address heroToken, uint heroTokenId, address caller);
  event NewHeroInited(address heroToken, uint heroTokenId, IStatController.ChangeableStats stats);
  event LevelUp(
    address heroToken,
    uint heroTokenId,
    uint heroClass,
    IStatController.CoreAttributes change
  );
  event ConsumableUsed(address heroToken, uint heroTokenId, address item);
  event RemoveConsumableUsage(address heroToken, uint heroTokenId, address item);
  event HeroCustomDataChanged(address token, uint tokenId, bytes32 index, uint value);
  event HeroCustomDataChangedNg(address token, uint tokenId, bytes32 index, uint value, uint8 ngLevel);
  event HeroCustomDataCleared(address token, uint tokenId);
  event GlobalCustomDataChanged(bytes32 index, uint value);
  //endregion ------------------ StatController

  //region ------------------ DungeonFactoryController
  event DungeonLaunched(
    uint16 dungeonLogicNum,
    uint64 dungeonId,
    address heroToken,
    uint heroTokenId,
    address treasuryToken,
    uint treasuryAmount
  );

  event BossCompleted(uint32 objectId, uint biome, address hero, uint heroId);
  event FreeDungeonAdded(uint8 biome, uint64 dungeonId);

  event ObjectOpened(uint64 dungId, address hero, uint id, uint32 objId, uint iteration, uint currentStage);
  event Clear(uint64 dungId);

  event DungeonLogicRegistered(uint16 dungLogicId, IDungeonFactory.DungeonGenerateInfo info);
  event DungeonLogicRemoved(uint16 dungLogicId);
  event DungeonSpecificLogicRegistered(uint16 dungLogicId, uint biome, uint heroCls);
  event DungeonSpecificLogicRemoved(uint16 dungLogicId, uint heroLvl, uint heroCls);
  event DungeonRegistered(uint16 dungLogicId, uint64 dungeonId);
  event DungeonRemoved(uint16 dungLogicId, uint64 dungeonId);
  event MinLevelForTreasuryChanged(address token, uint level);

  event ObjectAction(
    uint64 dungId,
    IGOC.ActionResult result,
    uint currentStage,
    address heroToken,
    uint heroTokenId,
    uint newStage
  );
  /// @notice On add the item to the dungeon
  event AddTreasuryItem(uint64 dungId, address itemAdr, uint itemId);
  event AddTreasuryToken(uint64 dungId, address token, uint amount);
  event ClaimToken(uint64 dungId, address token, uint amount);
  event ClaimItem(uint64 dungId, address token, uint id);

  event Entered(uint64 dungId, address hero, uint id);
  event DungeonCompleted(uint16 dungLogicNum, uint64 dungId, address hero, uint heroId);
  event Exit(uint64 dungId, bool claim);
  event ExitForcibly(uint64 dungId, address hero, uint heroId);
  event FreeDungeonRemoved(uint8 biome, uint64 dungeonId);
  event HeroCurrentDungeonChanged(address hero, uint heroId, uint64 dungeonId);
  //endregion ------------------ DungeonFactoryController

  //region ------------------ GameObjectController
  event EventRegistered(uint32 objectId, IGOC.EventRegInfo eventRegInfo);
  event StoryRegistered(uint32 objectId, uint16 storyId);
  event MonsterRegistered(uint32 objectId, IGOC.MonsterGenInfo monsterGenInfo);
  event ObjectRemoved(uint32 objectId);
  event ObjectResultEvent(
    uint64 dungeonId,
    uint32 objectId,
    IGOC.ObjectType objectType,
    address hero,
    uint heroId,
    uint8 stageId,
    uint iteration,
    bytes data,
    IGOC.ActionResult result,
    uint salt
  );
  //endregion ------------------ GameObjectController

  //region ------------------ StoryController
  event SetBurnItemsMeta(uint storyId, IStoryController.AnswerBurnRandomItemMeta meta);
  event SetNextObjRewriteMeta(uint storyId, IStoryController.NextObjRewriteMeta meta);
  event SetAnswersMeta(uint storyId, uint16[] answerPageIds, uint8[] answerHeroClasses, uint16[] answerIds);
  event SetAnswerNextPageMeta(uint storyId, IStoryController.AnswerNextPageMeta meta);
  event SetAnswerAttributeRequirements(uint storyId, IStoryController.AnswerAttributeRequirementsMeta meta);
  event SetAnswerItemRequirements(uint storyId, IStoryController.AnswerItemRequirementsMeta meta);
  event SetAnswerTokenRequirementsMeta(uint storyId, IStoryController.AnswerTokenRequirementsMeta meta);
  event SetAnswerAttributes(uint storyId, IStoryController.AnswerAttributesMeta meta);
  event SetAnswerHeroCustomDataRequirementMeta(uint storyId, IStoryController.AnswerCustomDataMeta meta);
  event SetAnswerGlobalCustomDataRequirementMeta(uint storyId, IStoryController.AnswerCustomDataMeta meta);
  event SetSuccessInfo(uint storyId, IStoryController.AnswerResultMeta meta);
  event SetFailInfo(uint storyId, IStoryController.AnswerResultMeta meta);
  event SetCustomDataResult(uint storyId, IStoryController.AnswerCustomDataResultMeta meta, IStoryController.CustomDataResult _type);
  event StoryCustomDataRequirements(uint storyId, bytes32 requiredCustomDataIndex, uint requiredCustomDataMinValue, uint requiredCustomDataMaxValue, bool requiredCustomDataIsHero);
  event StoryRequiredLevel(uint storyId, uint requiredLevel);
  event StoryFinalized(uint32 objectId, uint storyId);
  event StoryRemoved(uint32 objectId, uint storyId);

  event ItemBurned(
    address heroToken,
    uint heroTokenId,
    uint64 dungeonId,
    uint objectId,
    address nftToken,
    uint nftId,
    uint stageId,
    uint iteration
  );

  /// @notice Durability of the item was reduced to 0
  event ItemBroken(
    address heroToken,
    uint heroTokenId,
    uint64 dungeonId,
    uint objectId,
    address nftToken,
    uint nftId,
    uint stageId,
    uint iteration
  );


  event NotEquippedItemBurned(
    address heroToken,
    uint heroTokenId,
    uint64 dungeonId,
    uint storyId,
    address nftToken,
    uint nftId,
    uint stageId,
    uint iteration
  );

  event StoryChangeAttributes(
    uint32 objectId,
    address heroToken,
    uint heroTokenId,
    uint64 dungeonId,
    uint storyId,
    uint stageId,
    uint iteration,
    int32[] attributes
  );
  //endregion ------------------ StoryController

  //region ------------------------ HeroController
  event HeroRegistered(address hero, uint8 heroClass, address payToken, uint payAmount);
  event HeroCreatedNgp(address hero, uint heroId, string name, address owner, string refCode, uint8 tier, uint8 ngLevel);
  event BiomeChanged(address hero, uint heroId, uint8 biome);
  event LevelUp(address hero, uint heroId, address owner, IStatController.CoreAttributes change);
  event ReinforcementAsked(address hero, uint heroId, address helpHeroToken, uint helpHeroId);
  event GuildReinforcementAsked(address hero, uint heroId, address helpHeroToken, uint helpHeroId);
  event OtherItemGuildReinforcement(address item, uint itemId, address hero, uint heroId, address helpHeroToken, uint helpHeroId);
  event ReinforcementReleased(address hero, uint heroId, address helperToken, uint helperId);
  event GuildReinforcementReleased(address hero, uint heroId, address helperToken, uint helperId);
  event Killed(address hero, uint heroId, address killer, bytes32[] dropItems, uint dropTokenAmount);
  event Reborn(address hero, uint heroId, uint8 newNgLevel);
  event BossKilled(address account, address hero, uint heroId, uint8 biome, uint8 newNgLevel, bool reborn, uint rewardAmount);
  event TierSetup(uint8 tier, address hero, uint72 payAmount, uint8[] slots, address[][] items);
  //endregion ------------------------ HeroController

  //region ------------------------ FightLib
  event FightResultProcessed(
    address sender,
    IFightCalculator.FightInfoInternal result,
    IFightCalculator.FightCall callData,
    uint iteration
  );
  //endregion ------------------------ FightLib

  //region ------------------------ Oracle
  event Random(uint number, uint max);
  //endregion ------------------------ Oracle

  //region ------------------------ Controller
  event OfferGovernance(address newGov);
  event GovernanceAccepted(address gov);
  event StatControllerChanged(address value);
  event StoryControllerChanged(address value);
  event GameObjectControllerChanged(address value);
  event ReinforcementControllerChanged(address value);
  event OracleChanged(address value);
  event TreasuryChanged(address value);
  event ItemControllerChanged(address value);
  event HeroControllerChanged(address value);
  event GameTokenChanged(address value);
  event DungeonFactoryChanged(address value);
  event ProxyUpdated(address proxy, address logic);
  event Claimed(address token, uint amount);
  event TokenStatusChanged(address token, bool status);
  event UserControllerChanged(address value);
  event GuildControllerChanged(address value);
  event GameTokenPriceChanged(uint value);
  event RewardsPoolChanged(address value);
  event Process(address token, uint amount, address from, uint toBurn, uint toTreasury, uint toGov);
  //endregion ------------------------ Controller


  //region ------------------------ ReinforcementController
  event HeroStaked(address heroToken, uint heroId, uint biome, uint score);
  event HeroStakedV2(address heroToken, uint heroId, uint biome, uint rewardAmount);
  event HeroWithdraw(address heroToken, uint heroId);
  event HeroAsk(address heroToken, uint heroId);
  event HeroAskV2(address heroToken, uint heroId, uint hitsLast24h, uint fixedFee, uint helperRewardAmount);
  event TokenRewardRegistered(address heroToken, uint heroId, address token, uint amountAdded, uint totalAmount);
  event GuildTokenRewardRegistered(address heroToken, uint heroId, address token, uint amountAdded, uint guildId);
  event NftRewardRegistered(address heroToken, uint heroId, address token, uint id);
  event GuildNftRewardRegistered(address heroToken, uint heroId, address token, uint id, uint guildId);
  event ToHelperRatioChanged(uint value);
  event ClaimedToken(address heroToken, uint heroId, address token, uint amount, address recipient);
  event ClaimedItem(address heroToken, uint heroId, address item, uint itemId, address recipient);
  event MinLevelChanged(uint8 value);
  event MinLifeChancesChanged(uint value);
  //endregion ------------------------ ReinforcementController

  //region ------------------------ Treasury, reward pool
  event AssetsSentToDungeon(address dungeon, address token, uint amount);
  event RewardSentToUser(address receiver, address token, uint rewardAmount);
  event NotEnoughReward(address receiver, address token, uint rewardAmountToPay);
  event BaseAmountChanged(uint oldValue, uint newValue);
  //endregion ------------------------ Treasury, reward pool

  //region ------------------------ EventLib
  event EventResult(uint64 dungeonId, address heroToken, uint heroTokenId, uint8 stageId, IStatController.ActionInternalInfo gen, uint iteration);
  //endregion ------------------------ EventLib

  //region ------------------------ Item controller and helper contracts
  event ItemRegistered(address item, IItemController.RegisterItemParams info);
  event OtherItemRegistered(address item, IItemController.ItemMeta meta, bytes packedItemMetaData);
  event ItemRemoved(address item);
  event OtherItemRemoved(address item);
  event NewItemMinted(address item, uint itemId, IItemController.MintInfo info);
  event Equipped(address item, uint itemId, address heroToken, uint heroTokenId, uint8 itemSlot);
  event TakenOff(address item, uint itemId, address heroToken, uint heroTokenId, uint8 itemSlot, address destination);
  event ItemRepaired(address item, uint itemId, uint consumedItemId, uint16 baseDurability);
  event FailedToRepairItem(address item, uint itemId, uint consumedItemId, uint16 itemDurability);
  event Augmented(address item, uint itemId, uint consumedItemId, uint8 augLevel, IItemController.AugmentInfo info);
  event NotAugmented(address item, uint itemId, uint consumedItemId, uint8 augLevel);
  event ReduceDurability(address item, uint itemId, uint newDurability);
  event Used(address item, uint tokenId, address heroToken, uint heroTokenId);
  event Destroyed(address item, uint itemId);
  event FragilityReduced(address item, uint itemId, address consumedItem, uint consumedItemId, uint fragility);
  event ItemControllerHelper(address helper);
  event SetUnionConfig(uint configId, address[] items, uint[] count, address itemToMint);
  event RemoveUnionConfig(uint configId);
  event SetUnionKeyPass(address keyPassItem);
  event CombineItems(address msgSender, uint configId, address[] items, uint[][] itemIds, address mintedItem, uint mintedItemId);
  //endregion ------------------------ Item controller and helper contracts

  //region ------------------------ NFT and GameToken (only custom events, not ERC20/721 standards)
  event ChangePauseStatus(bool value);
  event MinterChanged(address value);

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

  event HeroMinted(uint heroId);
  event HeroBurned(uint heroId);
  event HeroUriByStatusChanged(string uri, uint statusLvl);

  event ItemMinted(uint tokenId);
  event ItemBurned(uint tokenId);
  event UriByRarityChanged(string uri, uint rarity);
  event SponsoredHeroCreated(address msgSender, address heroAddress, uint heroId, string heroName);
  //endregion ------------------------ NFT and GameToken (only custom events, not ERC20/721 standards)

  //region ------------------------ User controller
  event SetUserName(address user, string name);
  event SetUserAvatar(address user, string avatar);
  event LootBoxOpened(address user, uint lootBoxKind, address[] itemTokens, uint[] itemTokenIds);
  event LootBoxConfigChanged(uint lootBoxKind, address[] mintItems, uint32[] mintItemsChances, uint maxDropItems);
  event SetFeeRenaming(uint feeRenaming);
  event ActivityCompleted(address user, bool daily, bool weekly);
  event FameHallHeroRegistered(address hero, uint heroId, address heroOwner, uint8 openedNgLevel);
  //endregion ------------------------ User controller

  //region ------------------------ Guild

  event GuildCreated(address owner, uint guildId, string name, string urlLogo);
  event AddToGuild(uint guildId, address newUser);
  event ChangeGuildRights(uint guildId, address user, uint rights);
  event RemoveFromGuild(uint guildId, address user);
  event GuildDeleted(uint guildId);
  event GuildLevelUp(uint guildId, uint8 newLevel);
  event GuildRename(uint guildId, string newName);
  event GuildLogoChanged(uint guildId, string newLogoUrl);
  event GuildDescriptionChanged(uint guildId, string newDescription);
  event SetGuildRelation(uint guildId1, uint guildId2, bool peace);
  event TransferFromGuildBank(address user, address token, uint amount, address recipient);
  event TransferNftFromGuildBank(address user, address[] nfts, uint[] tokenIds, address recipient);
  event GuildBankDeployed(uint guildId, address guildBank);

  event SetToHelperRatio(uint guildId, uint8 value, address user);
  event TopUpGuildBank(address msgSender, uint guildId, address guildBank, uint amount);

  event GuildRequestRegistered(address msgSender, uint guildId, string userMessage, uint depositAmount);
  event GuildRequestStatusChanged(address msgSender, uint guildRequestId, uint8 newStatus, address user);
  event SetToHelperRatio(uint guildId, address msgSender, uint8 toHelperRatio);
  event SetGuildRequestDepositAmount(uint guildId, address msgSender, uint amount);
  event SetGuildBaseFee(uint fee);
  event SetPvpPointsCapacity(address msgSender, uint64 capacityPvpPoints, address[] users);
  event SetShelterController(address shelterController);
  event SetShelterAuction(address shelterAuction);
  event PayForBidFromGuildBank(uint guildId, uint amount, uint bid);
  //endregion ------------------------ Guild

  //region ------------------------ Guild shelter
  event RegisterShelter(uint sheleterId, uint price);
  event SetShelterItems(
    uint shelterId,
    address[] items,
    uint64[] pricesInPvpPoints,
    uint128[] pricesInGameTokens,
    uint16[] maxItemsPerDayThresholds
  );
  event RemoveShelterItems(uint shelterId, address[] items);
  event BuyShelter(uint guidlId, uint shelterId);
  event LeaveShelter(uint guildId, uint shelterId);
  event NewShelterBid(uint shelterId, uint buyerGuildId, uint amount);
  event RevokeShelterBid(uint shelterId);
  event UseShelterBid(uint shelterId, uint sellerGuildId, uint buyerGuidId, uint amount);
  event PurchaseShelterItem(address msgSender, address item, uint numSoldItems, uint priceInPvpPoints, uint priceInGameToken);
  event ChangeShelterOwner(uint shelterId, uint fromGuildId, uint toGuildId);
  event RestInShelter(address msgSender, address heroToken, uint heroTokenId);
  //endregion ------------------------ Guild shelter

  //region ------------------------ Guild reinforcement
  event GuildHeroStaked(address heroToken, uint heroId, uint guildId);
  event GuildHeroWithdrawn(address heroToken, uint heroId, uint guildId);
  event GuildHeroAsked(address heroToken, uint heroId, uint guildId, address user);

  /// @param user Address can be 0 if heroId was already burnt at the moment of reinforcement releasing
  event GuildHeroReleased(address heroToken, uint heroId, uint guildId, address user);
  //endregion ------------------------ Guild reinforcement

  //region ------------------------ Guild auction
  event AuctionPositionOpened(uint positionId, uint shelterId, uint sellerGuildId, address msgSender, uint minAuctionPrice);
  event AuctionPositionClosed(uint positionId, address msgSender);
  event AuctionBidOpened(uint bidId, uint positionId, uint amount, address msgSender);
  //endregion ------------------------ Guild auction

  //region ------------------------ Guild bank
  event GuildBankTransfer(address token, address recipient, uint amount);
  event GuildBankTransferNft(address to, address nft, uint tokenId);
  event GuildBankTransferNftMulti(address to, address[] nfts, uint[] tokenIds);
  //endregion ------------------------ Guild bank

  //region ------------------------ Pawnshop
  event PawnShopRouterDeployed(address pawnShop, address gameToken, address routerOwner, address deployed);
  event PawnShopRouterTransfer(address token, uint amount, address receiver);
  event PawnShopRouterBulkSell(address[] nfts, uint[] nftIds, uint[] prices, address nftOwner, uint[] positionIds);
  event PawnShopRouterClosePositions(uint[] positionIds, address receiver);
  event PawnShopRouterBulkBuy(uint[] positionIds, address receiver);

  //endregion ------------------------ Pawnshop
}

File 4 of 33 : IControllable.sol
// 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;

}

File 5 of 33 : IController.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;

interface IController {

  function governance() external view returns (address);

  function statController() external view returns (address);

  function storyController() external view returns (address);

  function gameObjectController() external view returns (address);

  function reinforcementController() external view returns (address);

  function oracle() external view returns (address);

  function treasury() external view returns (address);

  function itemController() external view returns (address);

  function heroController() external view returns (address);

  function dungeonFactory() external view returns (address);

  function gameToken() external view returns (address);

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

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

  function onPause() external view returns (bool);

  function userController() external view returns (address);

  function guildController() external view returns (address);

  function rewardsPool() external view returns (address);

  function gameTokenPrice() external view returns (uint);

  function process(address token, uint amount, address from) external;
}

File 6 of 33 : IDungeonFactory.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;

import "../openzeppelin/EnumerableSet.sol";
import "../openzeppelin/EnumerableMap.sol";

interface IDungeonFactory {

  /// @custom:storage-location erc7201:dungeon.factory.main
  struct MainState {
    /// @dev biome => dungeonLaunchedId
    mapping(uint => EnumerableSet.UintSet) freeDungeons;
    /// @dev hero + heroId + biome (packMapObject) -> completed
    mapping(bytes32 => bool) bossCompleted;
    /// @dev hero + heroId + dungNum (packDungeonKey) -> completed
    mapping(bytes32 => bool) specificDungeonCompleted;
    /// @notice Max biome completed by the hero
    /// @dev hero + heroId (nftPacked) -> max biome completed
    mapping(bytes32 => uint8) maxBiomeCompleted;
    /// @notice which dungeon the hero is currently in
    /// @dev hero+id => current DungeonId
    mapping(bytes32 => uint64) heroCurrentDungeon;

    // ---

    /// @notice Specific dungeon for the given pair of hero level + hero class
    ///         ALl specific dungeons are listed also in allSpecificDungeons
    /// @dev packUint8Array(specReqBiome, specReqHeroClass) => dungNum
    mapping(bytes32 => uint16) dungeonSpecific;
    /// @dev contains all specific dungNum for easy management
    EnumerableSet.UintSet allSpecificDungeons;
    /// @dev biome => dungNum
    mapping(uint8 => EnumerableSet.UintSet) dungeonsLogicByBiome;

    // ---

    /// @dev max available biome. auto-increment with new dung deploy
    uint8 maxBiome;

    /// @notice Address of treasure token => min hero level required
    /// @dev manual threshold for treasury
    mapping(address => uint) minLevelForTreasury;

    /// @notice Contains arrays for SKILL_1, SKILL_2, SKILL_3 with 0 or 1
    /// i.e. [0, 1, 0] means that durability of SKILL_2 should be reduced
    /// @dev hero + heroId => uint8[] array where idx = slotNum
    mapping(bytes32 => bytes32) skillSlotsForDurabilityReduction;

    /// @notice Counter of dungeons, it's incremented on launch of a new dungeon
    uint64 dungeonCounter;

    /// @dev dungNum = init attributes
    mapping(uint16 => DungeonAttributes) dungeonAttributes;
    /// @dev dungeonId => status
    mapping(uint64 => DungeonStatus) dungeonStatuses;

    /// @notice NG_LEVEL of the hero that has created the given dungeon
    mapping(uint64 dungeonId => uint ngLevel) dungeonNgLevel;
  }

  struct ObjectGenerateInfo {
    /// @notice List of chamber types for each unique object
    /// @dev uint8 types, packed using PackingLib.packUint8Array
    bytes32[] objTypesByStages;
    /// @notice List of chances for each chamber type
    /// @dev uint64 chances
    uint32[][] objChancesByStages;
  }

  struct DungeonGenerateInfo {
    /// @notice List of chamber types for each unique object
    uint8[][] objTypesByStages;
    /// @notice List of chances for each chamber type
    uint32[][] objChancesByStages;

    uint32[] uniqObjects;

    uint8 minLevel;
    uint8 maxLevel;

    bytes32[] requiredCustomDataIndex;
    uint64[] requiredCustomDataMinValue;
    uint64[] requiredCustomDataMaxValue;
    bool[] requiredCustomDataIsHero;
  }

  /// @notice Attributes of the given dungeon logic
  struct DungeonAttributes {
    /// @notice Total number of stages that should be passed to complete the dungeon
    uint8 stages;
    uint8 biome;

    /// @notice Default list of objects that should be passed in the dungeon
    uint32[] uniqObjects;

    /// @dev min+max (packUint8Array)
    bytes32 minMaxLevel;

    bytes32[] requiredCustomDataIndex;
    /// @notice Packed DungeonGenerateInfo.requiredCustomData: MinValue, MaxValue, IsHero
    /// @dev min+max+isHero(packStoryCustomDataRequirements)
    bytes32[] requiredCustomDataValue;

    ObjectGenerateInfo info;
  }

  /// @notice Current status of the given dungeon
  struct DungeonStatus {
    uint64 dungeonId;
    /// @notice Dungeon logic id
    uint16 dungNum;

    /// @notice True if the dungeon is completed by the hero
    bool isCompleted;

    /// @notice Hero in the dungeon or 0
    address heroToken;
    uint heroTokenId;
    /// @notice Current object that should be passed by the hero. 0 - new object is not opened
    uint32 currentObject;
    /// @notice Current stage in the dungeon that should be passed by the hero.
    uint8 currentStage;

    EnumerableMap.AddressToUintMap treasuryTokens;
    /// @notice All items that were minted on result of made actions
    bytes32[] treasuryItems;

    /// @notice Total number of stages that should be passed to complete the dungeon
    /// This value can be bigger than length of uniqObjects
    uint8 stages;
    /// @notice List of objects to be passed in the stage. The list can be dynamically changed during passing the stages
    uint32[] uniqObjects;
  }

  ////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////

  function launchForNewHero(address heroToken, uint heroTokenId, address owner) external returns (uint64 dungeonId);

  function maxBiomeCompleted(address heroToken, uint heroTokenId) external view returns (uint8);

  function currentDungeon(address heroToken, uint heroTokenId) external view returns (uint64);

  function skillSlotsForDurabilityReduction(address heroToken, uint heroTokenId) external view returns (uint8[] memory result);

  function setBossCompleted(uint32 objectId, address heroToken, uint heroTokenId, uint8 heroBiome) external;

  /// @notice Hero exists current dungeon forcibly same as when dying but without loosing life chance
  function exitForcibly(address heroToken, uint heroTokenId, address msgSender) external;

  function maxAvailableBiome() external view returns (uint8);

  function reborn(address hero, uint heroId) external;
}

File 7 of 33 : IERC165.sol
// 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);
}

File 8 of 33 : IERC721.sol
// 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;
}

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

pragma solidity ^0.8.0;

import "./IERC721.sol";

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

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

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

File 10 of 33 : IERC721Errors.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC-721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
 */
interface IERC721Errors {
  /**
   * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
  error ERC721InvalidOwner(address owner);

  /**
   * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
  error ERC721NonexistentToken(uint256 tokenId);

  /**
   * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
  error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

  /**
   * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
  error ERC721InvalidSender(address sender);

  /**
   * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
  error ERC721InvalidReceiver(address receiver);

  /**
   * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
  error ERC721InsufficientApproval(address operator, uint256 tokenId);

  /**
   * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
  error ERC721InvalidApprover(address approver);

  /**
   * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
  error ERC721InvalidOperator(address operator);
}

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

pragma solidity ^0.8.0;

import "./IERC721.sol";

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

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

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

File 12 of 33 : 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);
}

File 13 of 33 : IFightCalculator.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;

import "./IStatController.sol";
import "./IItemController.sol";

interface IFightCalculator {

  enum AttackType {
    UNKNOWN, // 0
    MELEE, // 1
    MAGIC, // 2
    SLOT_3,
    SLOT_4,
    SLOT_5,
    SLOT_6,
    SLOT_7,
    SLOT_8,
    SLOT_9,
    SLOT_10
  }

  /// @notice Attacker info: suitable both for hero and monsters
  struct AttackInfo {
    /// @notice Type of the attack
    /// by default, if attack token presents, it's magic attack and not-magic otherwise
    /// but this logic can become more complicated after introducing new attack types
    AttackType attackType;
    /// @notice NFT selected by hero for attack, it should be equip on.
    /// If attacker is a monster, this is a special case (stub NFT with zero ID is used)
    address attackToken;
    uint attackTokenId;
    address[] skillTokens;
    uint[] skillTokenIds;
  }

  struct FighterInfo {
    int32[] fighterAttributes;
    IStatController.ChangeableStats fighterStats;
    AttackType attackType;
    address attackToken;
    uint attackTokenId;
    uint race;
  }

  struct Statuses {
    bool stun;
    bool burn;
    bool freeze;
    bool confuse;
    bool curse;
    bool poison;
    bool gotCriticalHit;
    bool missed;
    bool hitBlocked;
  }

  struct FightResult {
    int32 healthA;
    int32 healthB;
    int32 manaConsumedA;
    int32 manaConsumedB;
  }

  struct FightCall {
    FighterInfo fighterA;
    FighterInfo fighterB;
    uint64 dungeonId;
    uint32 objectId;
    address heroAdr;
    uint heroId;
    uint8 stageId;
    uint iteration;
    uint8 turn;
  }

  struct SkillSlots {
    bool slot1;
    bool slot2;
    bool slot3;
  }

  //region ------------------------ FightLib-internal (FightInfoInternal is required by IApplicationEvents..)
  struct FightInfoInternal {
    Fighter fighterA;
    Fighter fighterB;
  }

  struct Fighter {
    IFightCalculator.FighterInfo info;
    IItemController.AttackInfo magicAttack;
    int32 health;
    int32 manaConsumed;
    int32 damage;
    int32 damagePoison;
    int32 damageReflect;
    IFightCalculator.Statuses statuses;
  }
  //endregion ------------------------ FightLib-internal

  function fight(FightCall memory callData) external returns (FightResult memory);
}

File 14 of 33 : IGOC.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;

import "../openzeppelin/EnumerableSet.sol";
import "./IController.sol";

interface IGOC {

  enum ObjectType {
    UNKNOWN, // 0
    EVENT, // 1
    MONSTER, // 2
    STORY, // 3
    END_SLOT
  }

  enum ObjectSubType {
    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,
    END_SLOT
  }

  /// @custom:storage-location erc7201:game.object.controller.main
  struct MainState {

    /// @dev objId = biome(00) type(00) id(0000) => biome(uint8) + objType(uint8)
    /// Id is id of the event, story or monster.
    mapping(uint32 => bytes32) objectMeta;

    /// @dev biome(uint8) + objType(uint8) => set of object id
    mapping(bytes32 => EnumerableSet.UintSet) objectIds;

    /// @dev heroAdr180 + heroId64 + cType8 + biome8 => set of already played objects. Should be cleared periodically
    mapping(bytes32 => EnumerableSet.UintSet) playedObjects;

    /// @dev HeroAdr(160) + heroId(uint64) + objId(uint32) => iteration count. It needs for properly emit events for every new entrance.
    mapping(bytes32 => uint) iterations;

    /// @dev objId(uint32) => EventInfo
    mapping(uint32 => EventInfo) eventInfos;

    /// @dev objId(uint32) => storyId
    mapping(uint32 => uint16) storyIds;

    /// @dev objId(uint32) => MonsterInfo
    mapping(uint32 => MonsterInfo) monsterInfos;

    /// @dev hero+id => last fight action timestamp
    mapping(bytes32 => uint) lastHeroFightTs;

    /// @dev delay for user actions in fight (suppose to prevent bot actions)
    uint fightDelay;
  }

  struct ActionResult {
    bool kill;
    bool completed;
    address heroToken;
    address[] mintItems;
    int32 heal;
    int32 manaRegen;
    int32 lifeChancesRecovered;
    int32 damage;
    int32 manaConsumed;
    uint32 objectId;
    uint32 experience;
    uint heroTokenId;
    uint iteration;
    uint32[] rewriteNextObject;
  }

  struct EventInfo {
    /// @dev chance to use good or bad attributes/stats
    uint32 goodChance;

    /// @dev toBytes32ArrayWithIds
    bytes32[] goodAttributes;
    bytes32[] badAttributes;

    /// @dev experience(uint32) + heal(int32) + manaRegen(int32) + lifeChancesRecovered(int32) + damage(int32) + manaConsume(int32) packStatsChange
    bytes32 statsChange;

    /// @dev item+chance packItemMintInfo
    bytes32[] mintItems;
  }

  struct MonsterInfo {
    /// @dev toBytes32ArrayWithIds
    bytes32[] attributes;
    /// @dev level(uint8) + race(uint8) + experience(uint32) + maxDropItems(uint8) packMonsterStats
    bytes32 stats;
    /// @dev attackToken(160) + attackTokenId(uint64) + attackType(uint8) packAttackInfo
    bytes32 attackInfo;

    /// @dev item+chance packItemMintInfo
    bytes32[] mintItems;

    /// @dev heroAdr(160) + heroId(uint64) => iteration => GeneratedMonster packed
    mapping(bytes32 => mapping(uint => bytes32)) _generatedMonsters;
  }

  struct MultiplierInfo {
    uint8 biome;
    /// @notice NG_LEVEL of the hero who is going to fight with the given monster
    /// Use type(uint8).max for !NG+
    uint8 heroNgLevel;
  }

  struct GeneratedMonster {
    bool generated;
    uint8 turnCounter;
    int32 hp;
    uint32 amplifier;
  }

  struct MonsterGenInfo {
    uint16 monsterId;
    uint8 biome;
    ObjectSubType subType;

    uint8[] attributeIds;
    int32[] attributeValues;

    uint8 level;
    uint8 race;
    uint32 experience;
    uint8 maxDropItems;

    address attackToken;
    uint64 attackTokenId;
    uint8 attackType;

    address[] mintItems;
    uint32[] mintItemsChances;
  }

  struct ActionContext {
    address sender;
    address heroToken;
    IController controller;
    uint8 biome;
    uint8 objectSubType;
    uint8 stageId;
    uint8 heroNgLevel;
    uint32 objectId;
    uint64 dungeonId;
    uint heroTokenId;
    uint salt;
    uint iteration;
    bytes data;
  }

  struct EventRegInfo {
    uint8 biome;
    uint16 eventId;
    ObjectSubType subType;

    uint32 goodChance;

    AttributeGenerateInfo goodAttributes;
    AttributeGenerateInfo badAttributes;

    uint32 experience;
    int32 heal;
    int32 manaRegen;
    int32 lifeChancesRecovered;
    int32 damage;
    int32 manaConsumed;

    address[] mintItems;
    uint32[] mintItemsChances;
  }

  struct AttributeGenerateInfo {
    uint8[] ids;
    int32[] values;
  }

  //////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////

  /// @dev represent object registration if non zero values
  function getObjectMeta(uint32 objectId) external view returns (uint8 biome, uint8 objectSubType);

  function isBattleObject(uint32 objectId) external view returns (bool);

  function getRandomObject(
    uint8[] memory cTypes,
    uint32[] memory chances,
    uint8 biomeLevel,
    address heroToken,
    uint heroTokenId
  ) external returns (uint32 objectId);

  function open(address heroToken, uint heroTokenId, uint32 objectId) external returns (uint iteration);

  function action(
    address sender,
    uint64 dungeonId,
    uint32 objectId,
    address heroToken,
    uint heroTokenId,
    uint8 stageId,
    bytes memory data
  ) external returns (ActionResult memory);

}

File 15 of 33 : IHeroController.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;
import "../openzeppelin/EnumerableSet.sol";
import "../openzeppelin/EnumerableMap.sol";

interface IHeroController {

  /// @custom:storage-location erc7201:hero.controller.main
  struct MainState {

    /// @dev A central place for all hero tokens
    /// @dev Deprecated. Controller is used instead.
    address heroTokensVault;

    /// @dev heroAdr => packed tokenAdr160+ amount96
    mapping(address => bytes32) payToken;

    /// @dev heroAdr => heroCls8
    mapping(address => uint8) heroClass;

    // ---

    /// @dev hero+id => individual hero name
    mapping(bytes32 => string) heroName;

    /// @dev name => hero+id, needs for checking uniq names
    mapping(string => bytes32) nameToHero;

    // ---

    /// @dev hero+id => biome
    mapping(bytes32 => uint8) heroBiome;

    /// @notice Exist reinforcement of any kind for the given hero
    /// @dev hero+id => packed reinforcement helper+id
    mapping(bytes32 => bytes32) reinforcementHero;

    /// @dev hero+id => reinforcement packed attributes
    mapping(bytes32 => bytes32[]) reinforcementHeroAttributes;

    /// @notice packedHero (hero + id) => count of calls of beforeTokenTransfer
    mapping(bytes32 => uint) countHeroTransfers;


    // ------------------------------------ NG plus

    /// @notice (tier, hero address) => TierInfo, where tier = [2, 3]
    /// @dev For tier=1 no data is required. Amount for tier 1 is stored in {payToken}, no items are minted
    /// Token from {payToken} is equal for all tiers
    mapping(bytes32 packedTierHero => TierInfo) tiers;

    mapping(bytes32 packedHero => HeroInfo) heroInfo;

    /// @notice Max NG_LVL reached by the heroes of a given account
    mapping(address user => uint8 maxNgLevel) maxUserNgLevel;

    /// @notice When the hero has killed boss on the given biome first time
    /// packedBiomeNgLevel = packed (biome, NG_LEVEL)
    mapping(bytes32 packedHero => mapping (bytes32 packedBiomeNgLevel => uint timestamp)) killedBosses;

    /// @notice Max NG_LEVEL reached by any user
    uint maxOpenedNgLevel;
  }

  /// @notice Tier = hero creation cost option
  /// There are 3 tiers:
  /// 1: most chip option, just pay fixed amount {payTokens} - new hero is created
  /// 2: pay bigger amount - random skill is equipped on the newly created hero
  /// 3: pay even more amount - random sill + some random items are equipped on the newly created hero
  struct TierInfo {
    /// @notice Cost of the hero creation using the given tier in terms of the token stored in {payToken}
    /// This amount is used for tiers 2, 3. For tier 1 the amount is taken from {payToken}
    uint amount;

    /// @notice All slots for which items-to-mint are registered in {itemsToMint}
    EnumerableSet.UintSet slots;

    /// @notice slot => items that can be minted and equipped on the hero to the given {slot} after hero creation
    mapping(uint8 slot => address[] items) itemsToMint;
  }

  /// @notice Current NG+-related values
  struct HeroInfo {
    /// @notice Hero tier = [0..3]. 0 - the hero is post-paid, it can be changed by upgrading the hero to pre-paid
    uint8 tier;

    /// @notice NG_LVL of the hero
    uint8 ngLevel;

    /// @notice True if hero has passed last biome on current NG+ and so NG_LEVEL can be incremented (reborn is allowed)
    bool rebornAllowed;

    /// @notice Amount paid for the hero on creation OR on upgrade to NG+
    /// Amount paid for creation of the hero in terms of game token (!NG+) is NOT stored here.
    /// @dev uint72 is used here to pack the whole struct to single slot
    uint72 paidAmount;

    /// @notice Pay token used to pay {paidAmount}
    address paidToken;
  }

  /// @notice Input data to create new hero
  struct HeroCreationData {
    /// @notice Desired NG_LVL of the hero
    uint8 ngLevel;

    /// @notice Desired tire of the newly created hero. Allowed values: [1..3]
    uint8 tier;

    /// @notice Enter to the dungeon after creation
    bool enter;

    /// @notice Desired hero name
    string heroName;

    /// @notice Optional: user account for which the hero is created
    address targetUserAccount;

    /// @notice Optional: ref-code to be passed to the hero-creation-related event
    string refCode;
  }


  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  function heroClass(address hero) external view returns (uint8);

  function heroBiome(address hero, uint heroId) external view returns (uint8);

  function payTokenInfo(address hero) external view returns (address token, uint amount);

  function heroReinforcementHelp(address hero, uint heroId) external view returns (address helperHeroToken, uint helperHeroId);

  function score(address hero, uint heroId) external view returns (uint);

  function isAllowedToTransfer(address hero, uint heroId) external view returns (bool);

  function beforeTokenTransfer(address hero, uint heroId) external returns (bool);

  // ---

  function create(address hero, string memory heroName_, bool enter) external returns (uint);

  function kill(address hero, uint heroId) external returns (bytes32[] memory dropItems);

  /// @notice Take off all items from the hero, reduce life to 1. The hero is NOT burnt.
  /// Optionally reduce mana to zero and/or decrease life chance.
  function softKill(address hero, uint heroId, bool decLifeChances, bool resetMana) external returns (bytes32[] memory dropItems);

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

  function resetLifeAndMana(address hero, uint heroId) external;

  function countHeroTransfers(address hero, uint heroId) external view returns (uint);

  function askGuildReinforcement(address hero, uint heroId, address helper, uint helperId) external;

  function getHeroInfo(address hero, uint heroId) external view returns (IHeroController.HeroInfo memory data);

  function registerKilledBoss(address hero, uint heroId, uint32 objectId) external;

  function maxOpenedNgLevel() external view returns (uint);
}

File 16 of 33 : IItem.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;

interface IItem {

  function isItem() external pure returns (bool);

  function mintFor(address recipient) external returns (uint tokenId);

  function burn(uint tokenId) external;

  function controlledTransfer(address from, address to, uint tokenId) external;
}

File 17 of 33 : IItemController.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;

import "./IStatController.sol";
import "./IGOC.sol";
import "../openzeppelin/EnumerableSet.sol";

interface IItemController {

  enum GlobalParam {
    UNKNOWN_0,

    /// @notice Address of ItemControllerHelper
    ITEM_CONTROLLER_HELPER_ADDRESS_1
  }

  /// @custom:storage-location erc7201:item.controller.main
  struct MainState {

    ////////////////// GENERATE //////////////////

    EnumerableSet.AddressSet items;

    /// @dev itemAdr => itemMetaType8 + itemLvl8 + itemType8 + baseDurability16 + defaultRarity8 + minAttr8 + maxAttr8 + manaCost32 + req(packed core 128)
    mapping(address => bytes32) itemMeta;

    /// @dev itemAdr => packed tokenAdr160+ amount96
    mapping(address => bytes32) augmentInfo;

    // --- common attr ---

    /// @dev itemAdr => id8 + min(int32) + max(int32) + chance32
    mapping(address => bytes32[]) generateInfoAttributes;

    // --- consumable ---

    /// @dev itemAdr => ids+values (toBytes32ArrayWithIds)
    mapping(address => bytes32[]) _itemConsumableAttributes;

    /// @dev itemAdr => IStatController.ChangeableStats packed int32[]
    mapping(address => bytes32) itemConsumableStats;

    // --- buff ---

    /// @dev itemAdr => id8 + min(int32) + max(int32) + chance32
    mapping(address => bytes32[]) generateInfoCasterAttributes;

    /// @dev itemAdr => id8 + minDmg(int32) + maxDmg(int32) + chance32
    mapping(address => bytes32[]) generateInfoTargetAttributes;

    // --- attack ---

    /// @dev itemAdr => packed AttackInfo: attackType8 + min32 + max32 + factors(packed core 128)
    mapping(address => bytes32) generateInfoAttack;

    ////////////////// ITEMS INFO //////////////////

    /// @dev itemAdr+id => itemRarity8 + augmentationLevel8 + itemDurability16
    mapping(bytes32 => bytes32) itemInfo;

    /// @dev itemAdr+id => heroAdr+id
    mapping(bytes32 => bytes32) equippedOn;

    // --- common attr ---

    /// @dev itemAdr+Id => ids+values (toBytes32ArrayWithIds)
    mapping(bytes32 => bytes32[]) _itemAttributes;

    // --- consumable ---

    // consumable stats unchangeable, get them by address

    // --- buff ---

    /// @dev itemAdr+Id => ids+values (toBytes32ArrayWithIds)
    mapping(bytes32 => bytes32[]) _itemCasterAttributes;

    /// @dev itemAdr+Id => ids+values (toBytes32ArrayWithIds)
    mapping(bytes32 => bytes32[]) _itemTargetAttributes;

    // --- attack ---

    /// @dev itemAdr+Id => packed AttackInfo: attackType8 + min32 + max32 + factors(packed core 128)
    mapping(bytes32 => bytes32) _itemAttackInfo;

    ////////////////// Additional generate info //////////////////

    /// @notice (itemAdr) => Bitmask of ConsumableActionBits
    mapping(address => uint) _consumableActionMask;


    /// --------------------------------- SIP-003: Item fragility
    /// @notice itemAdr + id => item fragility counter that displays the chance of an unsuccessful repair
    /// @dev [0...100_000], decimals 3
    mapping(bytes32 packedItem => uint fragility) itemFragility;

    /// @notice Universal mapping to store various addresses and numbers (params of the contract)
    mapping (GlobalParam param => uint value) globalParam;

    /// @notice Item address => packedMetadata
    /// {packedMetaData} is encoded using abi.encode/abi.decode
    /// Read first byte, detect meta data type by the byte value, apply proper decoder from PackingLib
    mapping(address item => bytes packedMetaData) packedItemMetaData;
  }

  struct RegisterItemParams {
    ItemMeta itemMeta;
    address augmentToken;
    uint augmentAmount;
    ItemGenerateInfo commonAttributes;

    IGOC.AttributeGenerateInfo consumableAttributes;
    IStatController.ChangeableStats consumableStats;

    ItemGenerateInfo casterAttributes;
    ItemGenerateInfo targetAttributes;

    AttackInfo genAttackInfo;
    /// @notice Bit mask of ConsumableActionBits
    uint consumableActionMask;
  }

  /// @notice Possible actions that can be triggered by using the consumable item
  enum ConsumableActionBits {
    CLEAR_TEMPORARY_ATTRIBUTES_0
    // other items are used instead this mask
  }

  struct ItemGenerateInfo {
    /// @notice Attribute ids
    uint8[] ids;
    /// @notice Min value of the attribute, != 0
    int32[] mins;
    /// @notice Max value of the attribute, != 0
    int32[] maxs;
    /// @notice Chance of the selection [0..MAX_CHANCES]
    uint32[] chances;
  }

  struct ItemMeta {
    uint8 itemMetaType;
    // Level in range 1-99. Reducing durability in low level dungeons. lvl/5+1 = biome
    uint8 itemLevel;
    IItemController.ItemType itemType;
    uint16 baseDurability;
    uint8 defaultRarity;
    uint32 manaCost;

    // it doesn't include positions with 100% chance
    uint8 minRandomAttributes;
    uint8 maxRandomAttributes;

    IStatController.CoreAttributes requirements;
  }

  // Deprecated. Todo - remove
  enum FeeType {
    UNKNOWN,
    REPAIR,
    AUGMENT,
    STORY,

    END_SLOT
  }

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

    END_SLOT
  }

  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
    OTHER, // 12

    END_SLOT
  }

  enum ItemMetaType {
    UNKNOWN, // 0
    COMMON, // 1
    ATTACK, // 2
    BUFF, // 3
    CONSUMABLE, // 4

    END_SLOT
  }

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

    END_SLOT
  }

  struct AttackInfo {
    AttackType aType;
    int32 min;
    int32 max;
    // if not zero - activate attribute factor for the attribute
    IStatController.CoreAttributes attributeFactors;
  }

  struct ItemInfo {
    ItemRarity rarity;
    uint8 augmentationLevel;
    uint16 durability;
  }

  /// @dev The struct is used in events, so it's moved here from the lib
  struct MintInfo {
    IItemController.ItemMeta meta;
    uint8[] attributesIds;
    int32[] attributesValues;
    IItemController.ItemRarity itemRarity;

    IItemController.AttackInfo attackInfo;

    uint8[] casterIds;
    int32[] casterValues;
    uint8[] targetIds;
    int32[] targetValues;
  }

  /// @dev The struct is used in events, so it's moved here from the lib
  struct AugmentInfo {
    uint8[] attributesIds;
    int32[] attributesValues;
    IItemController.AttackInfo attackInfo;
    uint8[] casterIds;
    int32[] casterValues;
    uint8[] targetIds;
    int32[] targetValues;
  }

  ///region ------------------------ Item type "Other"
  /// @notice Possible kinds of "Other" items
  /// Each "Other" item has each own structure for metadata, see OtherItemXXX
  enum OtherSubtypeKind {
    UNKNOWN_0,
    /// @notice Item to reduce fragility, see SCB-1014. Metadata is {OtherItemReduceFragility}
    REDUCE_FRAGILITY_1,

    /// @notice This item allows asking guild reinforcement to the guild member
    USE_GUILD_REINFORCEMENT_2,

    /// @notice Exit from dungeon (shelter of level 3 is required)
    EXIT_FROM_DUNGEON_3,

    /// @notice Rest in the shelter: restore of hp & mp, clear temporally attributes, clear used consumables (shelter of level 3 is required)
    REST_IN_SHELTER_4,

    /// @notice Stub item (i.e. OTHER_4) that has no logic in contracts, but it has correct (not empty) packedMetaData
    EMPTY_NO_LOGIC_5,

    END_SLOT
  }
  struct OtherItemReduceFragility {
    /// @notice "Other" item kind. It MUST BE first field in the struct.
    uint8 kind;

    /// @notice Value on which the fragility will be reduced.
    /// @dev [0...100%], decimals 3, so the value is in the range [0...10_000]
    uint248 value;
  }
  ///endregion ------------------------ Item type "Other"

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  function itemMeta(address item) external view returns (ItemMeta memory meta);

  function augmentInfo(address item) external view returns (address token, uint amount);

  function genAttributeInfo(address item) external view returns (ItemGenerateInfo memory info);

  function genCasterAttributeInfo(address item) external view returns (ItemGenerateInfo memory info);

  function genTargetAttributeInfo(address item) external view returns (ItemGenerateInfo memory info);

  function genAttackInfo(address item) external view returns (AttackInfo memory info);

  function itemInfo(address item, uint itemId) external view returns (ItemInfo memory info);

  function equippedOn(address item, uint itemId) external view returns (address hero, uint heroId);

  function itemAttributes(address item, uint itemId) external view returns (int32[] memory values, uint8[] memory ids);

  function consumableAttributes(address item) external view returns (int32[] memory values, uint8[] memory ids);

  function consumableStats(address item) external view returns (IStatController.ChangeableStats memory stats);

  function casterAttributes(address item, uint itemId) external view returns (int32[] memory values, uint8[] memory ids);

  function targetAttributes(address item, uint itemId) external view returns (int32[] memory values, uint8[] memory ids);

  function itemAttackInfo(address item, uint itemId) external view returns (AttackInfo memory info);

  function score(address item, uint tokenId) external view returns (uint);

  function isAllowedToTransfer(address item, uint tokenId) external view returns (bool);

  // ---

  function mint(address item, address recipient) external returns (uint itemId);

  function reduceDurability(address hero, uint heroId, uint8 biome, bool reduceDurabilityAllSkills) external;

  function destroy(address item, uint tokenId) external;

  function takeOffDirectly(
    address item,
    uint itemId,
    address hero,
    uint heroId,
    uint8 itemSlot,
    address destination,
    bool broken
  ) external;

  /// @notice SIP-003: item fragility counter that displays the chance of an unsuccessful repair.
  /// @dev [0...100%], decimals 3, so the value is in the range [0...10_000]
  function itemFragility(address item, uint itemId) external view returns (uint);

  /// @notice SIP-003: The quest mechanic that previously burned the item will increase its fragility by 1%
  function incBrokenItemFragility(address item, uint itemId) external;

  function equip(
    address hero,
    uint heroId,
    address[] calldata items,
    uint[] calldata itemIds,
    uint8[] calldata itemSlots
  ) external;
}

File 18 of 33 : IOracle.sol
// 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);

}

File 19 of 33 : IStatController.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;
import "../openzeppelin/EnumerableSet.sol";
import "../openzeppelin/EnumerableMap.sol";

interface IStatController {

  /// @custom:storage-location erc7201:stat.controller.main
  struct MainState {
    mapping(bytes32 => bytes32[]) heroTotalAttributes;
    /// @dev heroAdr+heroId => int32 packed strength, dexterity, vitality, energy
    mapping(bytes32 => bytes32) _heroCore;
    mapping(bytes32 => bytes32[]) heroBonusAttributes;
    mapping(bytes32 => bytes32[]) heroTemporallyAttributes;
    /// @dev heroAdr+heroId => uint32 packed level, experience, life, mana, lifeChances
    mapping(bytes32 => bytes32) heroStats;
    /// @dev heroAdr+heroId+itemSlot => itemAdr + itemId
    mapping(bytes32 => bytes32) heroSlots;
    /// @dev heroAdr+heroId => busy slots uint8[] packed
    mapping(bytes32 => bytes32) heroBusySlots;
    mapping(bytes32 => EnumerableSet.AddressSet) usedConsumables;
    /// @dev heroCustomDataV2 is used instead
    mapping(bytes32 => mapping(bytes32 => uint)) _deprecated_heroCustomData;
    mapping(bytes32 => uint) globalCustomData;

    /// @notice packNftIdWithValue(hero, heroId, ngLevel) => hero custom data map
    /// @dev initially it was packedHero => hero custom data map
    mapping(bytes32 => EnumerableMap.Bytes32ToUintMap) heroCustomDataV2;
  }


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

  // possible
  // HEAL_FACTOR

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

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

  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
    END_SLOT // 5
  }

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

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

  /// @dev This struct is used inside event, so it's moved here from lib
  struct ActionInternalInfo {
    int32[] posAttributes;
    int32[] negAttributes;

    uint32 experience;
    int32 heal;
    int32 manaRegen;
    int32 lifeChancesRecovered;
    int32 damage;
    int32 manaConsumed;

    address[] mintedItems;
  }

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

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

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

  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, uint64 tokenId, uint8 itemSlot) external view returns (bytes32 nftPacked);

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

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

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

  function changeHeroItemSlot(
    address heroToken,
    uint64 heroTokenId,
    uint itemType,
    uint8 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 (int32[] memory attributes, int32 manaConsumed);

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

  function setGlobalCustomData(bytes32 index, uint value) external;

  /// @notice Restore life and mana during reinforcement
  /// @dev Life and mana will be increased on ((current life/mana attr value) - (prev life/mana attr value))
  /// @param prevAttributes Hero attributes before reinforcement
  function restoreLifeAndMana(address heroToken, uint heroTokenId, int32[] memory prevAttributes) external;

  function reborn(address heroToken, uint heroTokenId, uint heroClass) external;
}

File 20 of 33 : IStoryController.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.23;

import "../interfaces/IGOC.sol";
import "../interfaces/IStatController.sol";
import "../interfaces/IItemController.sol";
import "./IController.sol";
import "./IOracle.sol";
import "./IHeroController.sol";
import "../openzeppelin/EnumerableSet.sol";

interface IStoryController {

  enum AnswerResultId {
    UNKNOWN, // 0
    SUCCESS, // 1
    ATTRIBUTE_FAIL, // 2
    RANDOM_FAIL, // 3
    DELAY_FAIL, // 4
    HERO_CUSTOM_DATA_FAIL, // 5
    GLOBAL_CUSTOM_DATA_FAIL, // 6

    END_SLOT
  }

  enum CustomDataResult {
    UNKNOWN, // 0
    HERO_SUCCESS, // 1
    HERO_FAIL, // 2
    GLOBAL_SUCCESS, // 3
    GLOBAL_FAIL, // 4

    END_SLOT
  }

  /// @custom:storage-location erc7201:story.controller.main
  struct MainState {

    // --- STORY REG INFO ---

    /// @dev Uniq story identification.
    mapping(uint32 => uint16) storyIds;
    /// @dev Revers mapping for stories for using in the next object rewrite logic.
    mapping(uint16 => uint32) idToStory;
    /// @dev Store used ids for stories.
    mapping(uint16 => bool) _usedStoryIds;
    /// @dev Prevent register the story twice
    mapping(uint32 => bool) registeredStories;

    // --- ANSWER MAPPING ---

    /// @dev storyId => all story pages. We need to have this mapping for properly remove meta info
    mapping(uint16 => EnumerableSet.UintSet) allStoryPages;

    /// @dev storyId => all possible answers. We need to have this mapping for properly remove meta info
    mapping(uint16 => EnumerableSet.Bytes32Set) allStoryAnswers;

    /// @dev storyId + pageId + heroClass (zero is default answers) => storyId + pageId + heroClass (zero is default answers) + answerId
    mapping(bytes32 => bytes32[]) answers;
    /// @dev answerUnPackedId + answerResultId => nextPageIds (will be chosen randomly from this array)
    ///      where answerResultId is:
    ///      0 - unknown,
    ///      1 - success,
    ///      2 - attr fail
    ///      3 - random fail
    ///      4 - delay fail
    ///      5 - hero custom data fail
    ///      6 - global custom data fail
    ///      see COUNT_ANSWER_RESULT_IDS
    mapping(bytes32 => uint16[]) nextPageIds;
    /// @dev story + pageId + heroClass (zero is default answers) => random nextObjs (adr + id, like packed nft id)
    mapping(bytes32 => uint32[]) nextObjectsRewrite;

    /// @dev answerPackedId => packed array of uint32[]
    ///      0 - random requirement(uint32, 1 - 99% success of this action, zero means no check)
    ///      1 - delay requirement(uint32, if time since the last call more than this value the check is fail, zero means no check)
    ///      2 - isFinalAnswer(uint8)
    mapping(bytes32 => bytes32) answerAttributes;

    // --- ANSWER REQUIREMENTS ---

    /// @dev answerPackedId => array of AttributeRequirementsPacked
    mapping(bytes32 => bytes32[]) attributeRequirements;
    /// @dev answerPackedId=> array of ItemRequirementsPacked
    mapping(bytes32 => bytes32[]) itemRequirements;
    /// @dev answerPackedId => array of TokenRequirementsPacked
    mapping(bytes32 => bytes32[]) tokenRequirements;
    /// @dev answerPackedId => custom data for hero
    mapping(bytes32 => CustomDataRequirementPacked[]) heroCustomDataRequirement;
    /// @dev answerPackedId => global custom data
    mapping(bytes32 => CustomDataRequirementPacked[]) globalCustomDataRequirement;

    // --- ANSWER RESULTS ---

    /// @dev answerPackedId => change attributes
    mapping(bytes32 => bytes32[]) successInfoAttributes;
    /// @dev answerPackedId => change stats
    mapping(bytes32 => bytes32) successInfoStats;
    /// @dev answerPackedId => mint items
    mapping(bytes32 => bytes32[]) successInfoMintItems;

    /// @dev answerPackedId => change attributes
    mapping(bytes32 => bytes32[]) failInfoAttributes;
    /// @dev answerPackedId => change stats
    mapping(bytes32 => bytes32) failInfoStats;
    /// @dev answerPackedId => mint items
    mapping(bytes32 => bytes32[]) failInfoMintItems;

    /// @dev answerUnPackedId + CustomDataResult => custom data array change
    ///      where CustomDataResult is
    ///      1 - hero success
    ///      2 - hero fail
    ///      3 - global success
    ///      4 - global fail
    ///      see COUNT_CUSTOM_DATA_RESULT_IDS
    mapping(bytes32 => bytes32[]) customDataResult;

    /// @notice answerPackedId => slot+chance+stopIfBurnt
    /// @dev Since SIP-003 the items are not burn but broke
    mapping(bytes32 => bytes32[]) burnItem;

    // --- GENERAL STORY REQUIREMENTS ---

    /// @dev story => Custom hero data requirements for a story. If exist and hero is not eligible should be not chose in a dungeon.
    mapping(uint => CustomDataRequirementRangePacked[]) storyRequiredHeroData;
    /// @dev story => Minimal level for the history. 0 means no requirements.
    mapping(uint => uint) storyRequiredLevel;

    // --- HERO STATES ---

    /// @dev hero + heroId + storyId => pageId + heroLastActionTS
    mapping(bytes32 => bytes32) heroState;

    // --- OTHER ---

    /// @dev storyId => build hash for the last update
    mapping(uint16 => uint) storyBuildHash;

    /// @notice Number of already minted items by the user within the given iteration of the story.
    /// Only minting of the given number of items is allowed per iteration (see MAX_MINTED_ITEMS_PER_ITERATION).
    /// @dev hero, heroId, story => mintedInIteration
    /// This map is not cleared: storyId:objectId is 1:1, each object has own sequence of iterations without duplicates
    mapping(bytes32 => mapping(uint iteration => uint countMintedItems)) mintedInIteration;
  }

  /// @dev We need to have flat structure coz Solidity can not handle arrays of structs properly
  struct StoryMetaInfo {
    uint16 storyId;

    // --- story reqs

    bytes32[] requiredCustomDataIndex;
    uint64[] requiredCustomDataMinValue;
    uint64[] requiredCustomDataMaxValue;
    bool[] requiredCustomDataIsHero;
    uint minLevel;

    // --- answer reqs

    AnswersMeta answersMeta;
    AnswerNextPageMeta answerNextPage;
    AnswerAttributeRequirementsMeta answerAttributeRequirements;
    AnswerItemRequirementsMeta answerItemRequirements;
    AnswerTokenRequirementsMeta answerTokenRequirements;
    AnswerAttributesMeta answerAttributes;
    AnswerCustomDataMeta answerHeroCustomDataRequirement;
    AnswerCustomDataMeta answerGlobalCustomDataRequirement;

    // --- answer results

    AnswerBurnRandomItemMeta answerBurnRandomItemMeta;
    NextObjRewriteMeta nextObjRewriteMeta;

    // --- story results

    AnswerResultMeta successInfo;
    AnswerResultMeta failInfo;

    AnswerCustomDataResultMeta successHeroCustomData;
    AnswerCustomDataResultMeta failHeroCustomData;
    AnswerCustomDataResultMeta successGlobalCustomData;
    AnswerCustomDataResultMeta failGlobalCustomData;
  }

  struct NextObjRewriteMeta {
    uint16[] nextObjPageIds;
    uint8[] nextObjHeroClasses;
    uint32[][] nextObjIds;
  }

  struct AnswersMeta {
    uint16[] answerPageIds;
    uint8[] answerHeroClasses;
    uint16[] answerIds;
  }

  struct AnswerNextPageMeta {
    uint16[] pageId;
    uint8[] heroClass;
    uint16[] answerId;
    uint8[] answerResultIds;
    uint16[][] answerNextPageIds;
  }

  struct AnswerAttributeRequirementsMeta {
    uint16[] pageId;
    uint8[] heroClass;
    uint16[] answerId;
    bool[][] cores;
    uint8[][] ids;
    int32[][] values;
  }

  struct AnswerItemRequirementsMeta {
    uint16[] pageId;
    uint8[] heroClass;
    uint16[] answerId;
    address[][] requireItems;
    bool[][] requireItemBurn;
    bool[][] requireItemEquipped;
  }

  struct AnswerTokenRequirementsMeta {
    uint16[] pageId;
    uint8[] heroClass;
    uint16[] answerId;
    address[][] requireToken;
    uint88[][] requireAmount;
    bool[][] requireTransfer;
  }

  struct AnswerAttributesMeta {
    uint16[] pageId;
    uint8[] heroClass;
    uint16[] answerId;
    uint32[] randomRequirements;
    uint32[] delayRequirements;
    bool[] isFinalAnswer;
  }

  struct AnswerCustomDataMeta {
    uint16[] pageId;
    uint8[] heroClass;
    uint16[] answerId;

    bytes32[][] dataIndexes;
    bool[][] mandatory;
    uint64[][] dataValuesMin;
    uint64[][] dataValuesMax;
  }

  struct AnswerResultMeta {
    uint16[] pageId;
    uint8[] heroClass;
    uint16[] answerId;

    uint8[][] attributeIds;
    /// @dev Max value is limitied by int24, see toBytes32ArrayWithIds impl
    int32[][] attributeValues;

    uint32[] experience;
    int32[] heal;
    int32[] manaRegen;
    int32[] lifeChancesRecovered;
    int32[] damage;
    int32[] manaConsumed;

    address[][] mintItems;
    uint32[][] mintItemsChances;
  }

  struct AnswerCustomDataResultMeta {
    uint16[] pageId;
    uint8[] heroClass;
    uint16[] answerId;

    bytes32[][] dataIndexes;
    int16[][] dataValues;
  }

  struct AnswerBurnRandomItemMeta {
    uint16[] pageId;
    uint8[] heroClass;
    uint16[] answerId;

    /// @notice 0 - random slot
    uint8[][] slots;
    /// @notice typical chances are [0..100] (no decimals here)
    uint64[][] chances;
    /// @notice Since SIP-003 the burning is replaced by breaking bu the name is kept as is
    bool[][] isStopIfBurnt;
  }

  struct CustomDataRequirementPacked {
    bytes32 index;
    /// @dev min(uint64) + max(uint64) + mandatory(uint8)
    bytes32 data;
  }

  struct CustomDataRequirementRangePacked {
    bytes32 index;
    /// @dev min(uint64) + max(uint64) + isHeroData(uint8)
    bytes32 data;
  }

  struct StatsChange {
    uint32 experience;
    int32 heal;
    int32 manaRegen;
    int32 lifeChancesRecovered;
    int32 damage;
    int32 manaConsumed;
  }

  struct StoryActionContext {
    uint stageId;
    uint iteration;
    bytes32 answerIdHash;
    bytes32 answerAttributes;
    address sender;
    address heroToken;
    IController controller;
    IStatController statController;
    IHeroController heroController;
    IOracle oracle;
    IItemController itemController;
    uint8 heroClass;
    uint8 heroClassFromAnswerHash;
    uint8 biome;
    uint16 storyId;
    uint16 storyIdFromAnswerHash;
    uint16 pageIdFromAnswerHash;
    uint16 answerNumber;
    uint16 pageId;
    uint32 objectId;
    uint64 dungeonId;
    uint40 heroLastActionTS;
    uint80 heroTokenId;
    IStatController.ChangeableStats heroStats;
  }

  // --- WRITE ---

  function storyAction(
    address sender,
    uint64 dungeonId,
    uint32 objectId,
    uint stageId,
    address heroToken,
    uint heroTokenId,
    uint8 biome,
    uint iteration,
    bytes memory data
  ) external returns (IGOC.ActionResult memory);

  // --- READ ---

  function isStoryAvailableForHero(uint32 objectId, address heroToken, uint heroTokenId) external view returns (bool);

  function idToStory(uint16 id) external view returns (uint32 objectId);

  function heroPage(address hero, uint80 heroId, uint16 storyId) external view returns (uint16 pageId);

  function storyIds(uint32 objectId) external view returns (uint16);

  function registeredStories(uint32 objectId) external view returns (bool);

}

File 21 of 33 : SlotsLib.sol
// 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)
    }
  }

}

File 22 of 33 : NftBase.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;

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

abstract contract NftBase is ERC721EnumerableUpgradeable, Controllable {

  //region ------------------------ Data types and constants
  /// @custom:storage-location erc7201:nft.base.storage
  struct NftBaseStorage {
    uint idCounter;
    string baseUri;
    mapping(uint => string) uniqueUri;
  }

  // keccak256(abi.encode(uint256(keccak256("nft.base.storage")) - 1)) & ~bytes32(uint256(0xff))
  bytes32 private constant NftBaseStorageLocation = 0xda2932ada77a3c8131d8c171a8679090714572b6a41aff2e2186c297ac0f5500;
  //endregion ------------------------ Data types and constants

  //region ------------------------ Initializer

  function __NftBase_init(
    string memory name_,
    string memory symbol_,
    address controller_,
    string memory uri
  ) internal onlyInitializing {
    _init(name_, symbol_, controller_, uri);
  }

  function _init(
    string memory name_,
    string memory symbol_,
    address controller_,
    string memory uri
  ) private {
    __ERC721_init(name_, symbol_);
    __Controllable_init(controller_);
    _getNftBaseStorage().idCounter = 1;
    _getNftBaseStorage().baseUri = uri;
    emit IApplicationEvents.BaseUriChanged(uri);
  }

  function _incrementAndGetId() internal returns (uint){
    uint id = _getNftBaseStorage().idCounter;
    // we are using uint64 id, so higher value will overflow in the game logic
    if (id + 1 >= uint(type(uint64).max)) revert IAppErrors.IdOverflow(id);
    _getNftBaseStorage().idCounter = id + 1;
    return id;
  }
  //endregion ------------------------ Initializer

  //region ------------------------ Restrictions
  function onlyDeployer() internal view {
    if (! IController(controller()).isDeployer(msg.sender)) revert IAppErrors.ErrorNotDeployer(msg.sender);
  }
  //endregion ------------------------ Restrictions

  //region ------------------------ Views

  function _getNftBaseStorage() private pure returns (NftBaseStorage storage $) {
    assembly {
      $.slot := NftBaseStorageLocation
    }
    return $;
  }

  function _baseURI() internal view override returns (string memory) {
    return _getNftBaseStorage().baseUri;
  }

  function exists(uint tokenId) external view returns (bool) {
    return _ownerOf(tokenId) != address(0);
  }

  function tokenURI(uint256 tokenId) public view override returns (string memory) {
    if (tokenId > _getNftBaseStorage().idCounter) revert IAppErrors.NotExistToken(tokenId);

    // unique uri used for concrete tokenId
    string memory uniqueURI = _getNftBaseStorage().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();
  }
  //endregion ------------------------ Views

  //region ------------------------ Gov actions

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

  function setBaseUri(string memory value) external {
    onlyDeployer();
    _getNftBaseStorage().baseUri = value;
    emit IApplicationEvents.BaseUriChanged(value);
  }
  //endregion ------------------------ Gov actions

  //region ------------------------ Internal logic

  function _beforeTokenTransfer(uint tokenId) internal virtual {
    // noop
  }

  function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address) {
    _beforeTokenTransfer(tokenId);
    return super._update(to, tokenId, auth);
  }
  //endregion ------------------------ Internal logic

}

File 23 of 33 : EnumerableMap.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableMap.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableMap.js.

pragma solidity ^0.8.20;

import {EnumerableSet} from "./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.
 *
 * ```solidity
 * 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 `UintToAddressMap` are just wrappers around the underlying Map.
  // This means that we can only create new EnumerableMaps for types that fit in bytes32.

  /**
   * @dev Query for a nonexistent map key.
     */
  error EnumerableMapNonexistentKey(bytes32 key);

  struct Bytes32ToBytes32Map {
    // Storage of keys
    EnumerableSet.Bytes32Set _keys;
    mapping(bytes32 key => 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];
    if (value == 0 && !contains(map, key)) {
      revert EnumerableMapNonexistentKey(key);
    }
    return value;
  }

  /**
   * @dev Return the an array containing all the keys
     *
     * 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
  function keys(Bytes32ToBytes32Map storage map) internal view returns (bytes32[] memory) {
    return map._keys.values();
  }

  // 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 map. 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 map. 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 Return the an array containing all the keys
     *
     * 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
  function keys(UintToUintMap storage map) internal view returns (uint256[] memory) {
    bytes32[] memory store = keys(map._inner);
    uint256[] memory result;

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

    return result;
  }

  // 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 map. 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 map. 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 Return the an array containing all the keys
     *
     * 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
  function keys(UintToAddressMap storage map) internal view returns (uint256[] memory) {
    bytes32[] memory store = keys(map._inner);
    uint256[] memory result;

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

    return result;
  }

  // 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 map. 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 map. 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 Return the an array containing all the keys
     *
     * 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
  function keys(AddressToUintMap storage map) internal view returns (address[] memory) {
    bytes32[] memory store = keys(map._inner);
    address[] memory result;

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

    return result;
  }

  // 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 map. 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 map. 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 Return the an array containing all the keys
     *
     * 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
  function keys(Bytes32ToUintMap storage map) internal view returns (bytes32[] memory) {
    bytes32[] memory store = keys(map._inner);
    bytes32[] memory result;

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

    return result;
  }
}

File 24 of 33 : EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

/**
 * @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.
 *
 * ```solidity
 * 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 is the index of the value in the `values` array plus 1.
    // Position 0 is used to mean a value is not in the set.
    mapping(bytes32 value => uint256) _positions;
  }

  /**
   * @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._positions[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 cache the value's position to prevent multiple reads from the same storage slot
    uint256 position = set._positions[value];

    if (position != 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 valueIndex = position - 1;
      uint256 lastIndex = set._values.length - 1;

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

        // Move the lastValue to the index where the value to delete is
        set._values[valueIndex] = lastValue;
        // Update the tracked position of the lastValue (that was just moved)
        set._positions[lastValue] = position;
      }

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

      // Delete the tracked position for the deleted slot
      delete set._positions[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._positions[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;
  }
}

File 25 of 33 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

import "../interfaces/IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC-165 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);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
  /**
   * @dev See {IERC165-supportsInterface}.
     */
  function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
    return interfaceId == type(IERC165).interfaceId;
  }
}

File 26 of 33 : ERC721EnumerableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/ERC721Enumerable.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev This implements an optional extension of {ERC721} defined in the ERC that adds enumerability
 * of all the token ids in the contract as well as all token ids owned by each account.
 *
 * CAUTION: {ERC721} extensions that implement custom `balanceOf` logic, such as {ERC721Consecutive},
 * interfere with enumerability and should not be used together with {ERC721Enumerable}.
 */
abstract contract ERC721EnumerableUpgradeable is Initializable, ERC721Upgradeable, IERC721Enumerable {
  /// @custom:storage-location erc7201:openzeppelin.storage.ERC721Enumerable
  struct ERC721EnumerableStorage {
    mapping(address owner => mapping(uint256 index => uint256)) _ownedTokens;
    mapping(uint256 tokenId => uint256) _ownedTokensIndex;

    uint256[] _allTokens;
    mapping(uint256 tokenId => uint256) _allTokensIndex;
  }

  // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC721Enumerable")) - 1)) & ~bytes32(uint256(0xff))
  bytes32 private constant ERC721EnumerableStorageLocation = 0x645e039705490088daad89bae25049a34f4a9072d398537b1ab2425f24cbed00;

  function _getERC721EnumerableStorage() private pure returns (ERC721EnumerableStorage storage $) {
    assembly {
      $.slot := ERC721EnumerableStorageLocation
    }
    return $;
  }

  /**
   * @dev An `owner`'s token query was out of bounds for `index`.
     *
     * NOTE: The owner being `address(0)` indicates a global out of bounds index.
     */
  error ERC721OutOfBoundsIndex(address owner, uint256 index);

  /**
   * @dev Batch mint is not allowed.
     */
  error ERC721EnumerableForbiddenBatchMint();

  function __ERC721Enumerable_init() internal onlyInitializing {
  }

  function __ERC721Enumerable_init_unchained() internal onlyInitializing {
  }
  /**
   * @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 returns (uint256) {
    ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();
    if (index >= balanceOf(owner)) {
      revert ERC721OutOfBoundsIndex(owner, index);
    }
    return $._ownedTokens[owner][index];
  }

  /**
   * @dev See {IERC721Enumerable-totalSupply}.
     */
  function totalSupply() public view virtual returns (uint256) {
    ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();
    return $._allTokens.length;
  }

  /**
   * @dev See {IERC721Enumerable-tokenByIndex}.
     */
  function tokenByIndex(uint256 index) public view virtual returns (uint256) {
    ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();
    if (index >= totalSupply()) {
      revert ERC721OutOfBoundsIndex(address(0), index);
    }
    return $._allTokens[index];
  }

  /**
   * @dev See {ERC721-_update}.
     */
  function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address) {
    address previousOwner = super._update(to, tokenId, auth);

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

    return previousOwner;
  }

  /**
   * @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 {
    ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();
    uint256 length = balanceOf(to) - 1;
    $._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 {
    ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();
    $._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 {
    ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();
    // 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 = balanceOf(from);
    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 {
    ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();
    // 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();
  }

  /**
   * See {ERC721-_increaseBalance}. We need that to account tokens that were minted in batch
   */
  function _increaseBalance(address account, uint128 amount) internal virtual override {
    if (amount > 0) {
      revert ERC721EnumerableForbiddenBatchMint();
    }
    super._increaseBalance(account, amount);
  }
}

File 27 of 33 : ERC721Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.20;

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

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

  /// @custom:storage-location erc7201:openzeppelin.storage.ERC721
  struct ERC721Storage {
    // Token name
    string _name;

    // Token symbol
    string _symbol;

    mapping(uint256 tokenId => address) _owners;

    mapping(address owner => uint256) _balances;

    mapping(uint256 tokenId => address) _tokenApprovals;

    mapping(address owner => mapping(address operator => bool)) _operatorApprovals;
  }

  // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC721")) - 1)) & ~bytes32(uint256(0xff))
  bytes32 private constant ERC721StorageLocation = 0x80bb2b638cc20bc4d0a60d66940f3ab4a00c1d7b313497ca82fb0b4ab0079300;

  function _getERC721Storage() private pure returns (ERC721Storage storage $) {
    assembly {
      $.slot := ERC721StorageLocation
    }
    return $;
  }

  /**
   * @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 {
    ERC721Storage storage $ = _getERC721Storage();
    $._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 returns (uint256) {
    ERC721Storage storage $ = _getERC721Storage();
    if (owner == address(0)) {
      revert ERC721InvalidOwner(address(0));
    }
    return $._balances[owner];
  }

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

  /**
   * @dev See {IERC721Metadata-name}.
     */
  function name() public view virtual returns (string memory) {
    ERC721Storage storage $ = _getERC721Storage();
    return $._name;
  }

  /**
   * @dev See {IERC721Metadata-symbol}.
     */
  function symbol() public view virtual returns (string memory) {
    ERC721Storage storage $ = _getERC721Storage();
    return $._symbol;
  }

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

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

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

  /**
   * @dev See {IERC721-approve}.
     */
  function approve(address to, uint256 tokenId) public virtual {
    _approve(to, tokenId, _msgSender());
  }

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

    return _getApproved(tokenId);
  }

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

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

  /**
   * @dev See {IERC721-transferFrom}.
     */
  function transferFrom(address from, address to, uint256 tokenId) public virtual {
    if (to == address(0)) {
      revert ERC721InvalidReceiver(address(0));
    }
    // Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists
    // (from != 0). Therefore, it is not needed to verify that the return value is not 0 here.
    address previousOwner = _update(to, tokenId, _msgSender());
    if (previousOwner != from) {
      revert ERC721IncorrectOwner(from, tokenId, previousOwner);
    }
  }

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

  /**
   * @dev See {IERC721-safeTransferFrom}.
     */
  function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual {
    transferFrom(from, to, tokenId);
    _checkOnERC721Received(from, to, tokenId, data);
  }

  /**
   * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
     *
     * IMPORTANT: Any overrides to this function that add ownership of tokens not tracked by the
     * core ERC-721 logic MUST be matched with the use of {_increaseBalance} to keep balances
     * consistent with ownership. The invariant to preserve is that for any address `a` the value returned by
     * `balanceOf(a)` must be equal to the number of tokens such that `_ownerOf(tokenId)` is `a`.
     */
  function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
    ERC721Storage storage $ = _getERC721Storage();
    return $._owners[tokenId];
  }

  /**
   * @dev Returns the approved address for `tokenId`. Returns 0 if `tokenId` is not minted.
     */
  function _getApproved(uint256 tokenId) internal view virtual returns (address) {
    ERC721Storage storage $ = _getERC721Storage();
    return $._tokenApprovals[tokenId];
  }

  /**
   * @dev Returns whether `spender` is allowed to manage `owner`'s tokens, or `tokenId` in
     * particular (ignoring whether it is owned by `owner`).
     *
     * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
     * assumption.
     */
  function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) {
    return
      spender != address(0) &&
      (owner == spender || isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender);
  }

  /**
   * @dev Checks if `spender` can operate on `tokenId`, assuming the provided `owner` is the actual owner.
     * Reverts if `spender` does not have approval from the provided `owner` for the given token or for all its assets
     * the `spender` for the specific `tokenId`.
     *
     * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
     * assumption.
     */
  function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual {
    if (!_isAuthorized(owner, spender, tokenId)) {
      if (owner == address(0)) {
        revert ERC721NonexistentToken(tokenId);
      } else {
        revert ERC721InsufficientApproval(spender, tokenId);
      }
    }
  }

  /**
   * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
     *
     * NOTE: the value is limited to type(uint128).max. This protect against _balance overflow. It is unrealistic that
     * a uint256 would ever overflow from increments when these increments are bounded to uint128 values.
     *
     * WARNING: Increasing an account's balance using this function tends to be paired with an override of the
     * {_ownerOf} function to resolve the ownership of the corresponding tokens so that balances and ownership
     * remain consistent with one another.
     */
  function _increaseBalance(address account, uint128 value) internal virtual {
    ERC721Storage storage $ = _getERC721Storage();
    unchecked {
      $._balances[account] += value;
    }
  }

  /**
   * @dev Transfers `tokenId` from its current owner to `to`, or alternatively mints (or burns) if the current owner
     * (or `to`) is the zero address. Returns the owner of the `tokenId` before the update.
     *
     * The `auth` argument is optional. If the value passed is non 0, then this function will check that
     * `auth` is either the owner of the token, or approved to operate on the token (by the owner).
     *
     * Emits a {Transfer} event.
     *
     * NOTE: If overriding this function in a way that tracks balances, see also {_increaseBalance}.
     */
  function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) {
    ERC721Storage storage $ = _getERC721Storage();
    address from = _ownerOf(tokenId);

    // Perform (optional) operator check
    if (auth != address(0)) {
      _checkAuthorized(from, auth, tokenId);
    }

    // Execute the update
    if (from != address(0)) {
      // Clear approval. No need to re-authorize or emit the Approval event
      _approve(address(0), tokenId, address(0), false);

      unchecked {
        $._balances[from] -= 1;
      }
    }

    if (to != address(0)) {
      unchecked {
        $._balances[to] += 1;
      }
    }

    $._owners[tokenId] = to;

    emit Transfer(from, to, tokenId);

    return from;
  }

  /**
   * @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 {
    if (to == address(0)) {
      revert ERC721InvalidReceiver(address(0));
    }
    address previousOwner = _update(to, tokenId, address(0));
    if (previousOwner != address(0)) {
      revert ERC721InvalidSender(address(0));
    }
  }

  /**
   * @dev Mints `tokenId`, transfers it to `to` and checks for `to` acceptance.
     *
     * 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 {
    _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);
    _checkOnERC721Received(address(0), to, tokenId, data);
  }

  /**
   * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     * This is an internal function that does not check if the sender is authorized to operate on the token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
  function _burn(uint256 tokenId) internal {
    address previousOwner = _update(address(0), tokenId, address(0));
    if (previousOwner == address(0)) {
      revert ERC721NonexistentToken(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 {
    if (to == address(0)) {
      revert ERC721InvalidReceiver(address(0));
    }
    address previousOwner = _update(to, tokenId, address(0));
    if (previousOwner == address(0)) {
      revert ERC721NonexistentToken(tokenId);
    } else if (previousOwner != from) {
      revert ERC721IncorrectOwner(from, tokenId, previousOwner);
    }
  }

  /**
   * @dev Safely transfers `tokenId` token from `from` to `to`, checking that contract recipients
     * are aware of the ERC-721 standard 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 like {safeTransferFrom} in the sense that it invokes
     * {IERC721Receiver-onERC721Received} on the receiver, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `tokenId` token must exist and be owned by `from`.
     * - `to` cannot be the zero address.
     * - `from` cannot be the zero address.
     * - 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) internal {
    _safeTransfer(from, to, tokenId, "");
  }

  /**
   * @dev Same as {xref-ERC721-_safeTransfer-address-address-uint256-}[`_safeTransfer`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
  function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
    _transfer(from, to, tokenId);
    _checkOnERC721Received(from, to, tokenId, data);
  }

  /**
   * @dev Approve `to` to operate on `tokenId`
     *
     * The `auth` argument is optional. If the value passed is non 0, then this function will check that `auth` is
     * either the owner of the token, or approved to operate on all tokens held by this owner.
     *
     * Emits an {Approval} event.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
  function _approve(address to, uint256 tokenId, address auth) internal {
    _approve(to, tokenId, auth, true);
  }

  /**
   * @dev Variant of `_approve` with an optional flag to enable or disable the {Approval} event. The event is not
     * emitted in the context of transfers.
     */
  function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual {
    ERC721Storage storage $ = _getERC721Storage();
    // Avoid reading the owner unless necessary
    if (emitEvent || auth != address(0)) {
      address owner = _requireOwned(tokenId);

      // We do not use _isAuthorized because single-token approvals should not be able to call approve
      if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) {
        revert ERC721InvalidApprover(auth);
      }

      if (emitEvent) {
        emit Approval(owner, to, tokenId);
      }
    }

    $._tokenApprovals[tokenId] = to;
  }

  /**
   * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Requirements:
     * - operator can't be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
  function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
    ERC721Storage storage $ = _getERC721Storage();
    if (operator == address(0)) {
      revert ERC721InvalidOperator(operator);
    }
    $._operatorApprovals[owner][operator] = approved;
    emit ApprovalForAll(owner, operator, approved);
  }

  /**
   * @dev Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned).
     * Returns the owner.
     *
     * Overrides to ownership logic should be done to {_ownerOf}.
     */
  function _requireOwned(uint256 tokenId) internal view returns (address) {
    address owner = _ownerOf(tokenId);
    if (owner == address(0)) {
      revert ERC721NonexistentToken(tokenId);
    }
    return owner;
  }

  /**
   * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target address. This will revert if the
     * recipient doesn't accept the token transfer. 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
     */
  function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private {
    if (to.code.length > 0) {
      try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
        if (retval != IERC721Receiver.onERC721Received.selector) {
          revert ERC721InvalidReceiver(to);
        }
      } catch (bytes memory reason) {
        if (reason.length == 0) {
          revert ERC721InvalidReceiver(to);
        } else {
          /// @solidity memory-safe-assembly
          assembly {
            revert(add(32, reason), mload(reason))
          }
        }
      }
    }
  }
}

File 28 of 33 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @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.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * 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 prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
  /**
   * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
  struct InitializableStorage {
    /**
     * @dev Indicates that the contract has been initialized.
         */
    uint64 _initialized;
    /**
     * @dev Indicates that the contract is in the process of being initialized.
         */
    bool _initializing;
  }

  // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
  bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

  /**
   * @dev The contract is already initialized.
     */
  error InvalidInitialization();

  /**
   * @dev The contract is not initializing.
     */
  error NotInitializing();

  /**
   * @dev Triggered when the contract has been initialized or reinitialized.
     */
  event Initialized(uint64 version);

  /**
   * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
  modifier initializer() {
    // solhint-disable-next-line var-name-mixedcase
    InitializableStorage storage $ = _getInitializableStorage();

    // Cache values to avoid duplicated sloads
    bool isTopLevelCall = !$._initializing;
    uint64 initialized = $._initialized;

    // Allowed calls:
    // - initialSetup: the contract is not in the initializing state and no previous version was
    //                 initialized
    // - construction: the contract is initialized at version 1 (no reininitialization) and the
    //                 current contract is just being deployed
    bool initialSetup = initialized == 0 && isTopLevelCall;
    bool construction = initialized == 1 && address(this).code.length == 0;

    if (!initialSetup && !construction) {
      revert InvalidInitialization();
    }
    $._initialized = 1;
    if (isTopLevelCall) {
      $._initializing = true;
    }
    _;
    if (isTopLevelCall) {
      $._initializing = false;
      emit Initialized(1);
    }
  }

  /**
   * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
  modifier reinitializer(uint64 version) {
    // solhint-disable-next-line var-name-mixedcase
    InitializableStorage storage $ = _getInitializableStorage();

    if ($._initializing || $._initialized >= version) {
      revert InvalidInitialization();
    }
    $._initialized = version;
    $._initializing = true;
    _;
    $._initializing = false;
    emit Initialized(version);
  }

  /**
   * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
  modifier onlyInitializing() {
    _checkInitializing();
    _;
  }

  /**
   * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
  function _checkInitializing() internal view virtual {
    if (!_isInitializing()) {
      revert NotInitializing();
    }
  }

  /**
   * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
  function _disableInitializers() internal virtual {
    // solhint-disable-next-line var-name-mixedcase
    InitializableStorage storage $ = _getInitializableStorage();

    if ($._initializing) {
      revert InvalidInitialization();
    }
    if ($._initialized != type(uint64).max) {
      $._initialized = type(uint64).max;
      emit Initialized(type(uint64).max);
    }
  }

  /**
   * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
  function _getInitializedVersion() internal view returns (uint64) {
    return _getInitializableStorage()._initialized;
  }

  /**
   * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
  function _isInitializing() internal view returns (bool) {
    return _getInitializableStorage()._initializing;
  }

  /**
   * @dev Returns a pointer to the storage namespace.
     */
  // solhint-disable-next-line var-name-mixedcase
  function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
    assembly {
      $.slot := INITIALIZABLE_STORAGE
    }
  }
}

File 29 of 33 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {

  /**
    * @dev Muldiv operation overflow.
   */
  error MathOverflowedMulDiv();

  enum Rounding {
    Floor, // Toward negative infinity
    Ceil, // Toward positive infinity
    Trunc, // Toward zero
    Expand // Away from zero
  }

  /**
   * @dev Returns the addition of two unsigned integers, with an success flag (no overflow).
     */
  function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
    unchecked {
      uint256 c = a + b;
      if (c < a) return (false, 0);
      return (true, c);
    }
  }

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

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

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

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

  /**
   * @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 towards infinity instead
     * of rounding towards zero.
     */
  function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
    if (b == 0) {
      // Guarantee the same behavior as in a regular Solidity division.
      return a / b;
    }

    // The following calculation ensures accurate ceiling division without overflow.
    // Since a is non-zero, (a - 1) / b will not overflow.
    // The largest possible result occurs when (a - 1) / b is type(uint256).max,
    // but the largest value we can obtain is type(uint256).max - 1, which happens
    // when a = type(uint256).max and b = 1.
    unchecked {
      return a == 0 ? 0 : (a - 1) / b + 1;
    }
  }

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

    // Handle non-overflow cases, 256 by 256 division.
      if (prod1 == 0) {
        // Solidity will revert if denominator == 0, unlike the div opcode on its own.
        // The surrounding unchecked block does not change this fact.
        // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
        return prod0 / denominator;
      }

    // Make sure the result is less than 2^256. Also prevents denominator == 0.
      if (denominator <= prod1) {
        revert MathOverflowedMulDiv();
      }

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

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

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

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

      uint256 twos = denominator & (0 - denominator);
      assembly {
      // Divide denominator by twos.
        denominator := div(denominator, twos)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  /**
   * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
  function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
    return uint8(rounding) % 2 == 1;
  }

}

File 30 of 33 : SignedMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
  /**
   * @dev Returns the largest of two signed numbers.
     */
  function max(int256 a, int256 b) internal pure returns (int256) {
    return a > b ? a : b;
  }

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

  /**
   * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
  function average(int256 a, int256 b) internal pure returns (int256) {
    // Formula from the book "Hacker's Delight"
    int256 x = (a & b) + ((a ^ b) >> 1);
    return x + (int256(uint256(x) >> 255) & (a ^ b));
  }

  /**
   * @dev Returns the absolute unsigned value of a signed value.
     */
  function abs(int256 n) internal pure returns (uint256) {
    unchecked {
    // must be unchecked in order to support `n = type(int256).min`
      return uint256(n >= 0 ? n : -n);
    }
  }
}

File 31 of 33 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)

pragma solidity ^0.8.20;

import "./Math.sol";
import "./SignedMath.sol";

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

  /**
   * @dev The `value` string doesn't fit in the specified `length`.
     */
  error StringsInsufficientHexLength(uint256 value, uint256 length);

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

  /**
   * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
  function toStringSigned(int256 value) internal pure returns (string memory) {
    return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
  }

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

  /**
   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
  function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
    uint256 localValue = value;
    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_DIGITS[localValue & 0xf];
      localValue >>= 4;
    }
    if (localValue != 0) {
      revert StringsInsufficientHexLength(value, length);
    }
    return string(buffer);
  }

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

  /**
   * @dev Returns true if the two strings are equal.
     */
  function equal(string memory a, string memory b) internal pure returns (bool) {
    return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
  }
}

File 32 of 33 : Controllable.sol
// 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_) internal onlyInitializing {
    _init(controller_);
  }

  function _init(address controller_) private {
    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);
  }

}

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

pragma solidity ^0.8.1;

import "../interfaces/IAppErrors.sol";

/**
 * @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_1_BALANCE_ERC_2771 = 0xd8253782c45a12053594b9deB72d8e8aB2Fca54c;
  address private constant SACRA_RELAY = 0x52CEba41Da235Af367bFC0b0cCd3314cb901bB5F;
  address private constant SACRA_RELAY_2 = 0x102f1f556cD9C3D5f820E6920A8931657c5Da21B;

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

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

  function onlyEOA() internal view {
    if (!_isNotSmartContract()) {
      revert IAppErrors.NotEOA(msg.sender);
    }
  }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[],"name":"ERC721EnumerableForbiddenBatchMint","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721IncorrectOwner","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721InsufficientApproval","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC721InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"ERC721InvalidOperator","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721InvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC721InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC721InvalidSender","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721NonexistentToken","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"ERC721OutOfBoundsIndex","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"EquippedItemIsNotAllowedToTransfer","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ErrorNotDeployer","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ErrorNotItemController","type":"error"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"IdOverflow","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"NotExistToken","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"uri","type":"string"}],"name":"BaseUriChanged","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":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ItemBurned","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ItemMinted","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":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"},{"inputs":[],"name":"CONTROLLABLE_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":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"controlledTransfer","outputs":[],"stateMutability":"nonpayable","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":"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"}],"name":"init","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":[{"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":"forwarder","type":"address"}],"name":"isTrustedForwarder","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"mintFor","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":"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":[],"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":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"value","type":"string"}],"name":"setBaseUri","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"uri","type":"string"},{"internalType":"uint8","name":"rarity","type":"uint8"}],"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":"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"}]

60806040523480156200001157600080fd5b507ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff1615906001600160401b03166000811580156200005d5750825b90506000826001600160401b031660011480156200007a5750303b155b90508115801562000089575080155b15620000a85760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b03191660011785558315620000d757845460ff60401b1916680100000000000000001785555b83156200011e57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505061288880620001336000396000f3fe608060405234801561001057600080fd5b50600436106101e75760003560e01c80636c0360eb11610110578063a22cb465116100a8578063a22cb465146103d9578063a60138a7146103ec578063b429afeb146103ff578063b88d4fde14610412578063c87b56dd14610425578063dee1f0e414610438578063e985e9c51461044b578063f77c47911461045e578063fcbdbaaa14610466578063ffa1ad741461047957600080fd5b80636c0360eb1461034957806370a082311461035157806371e578dc146103645780637746fa28146103775780637cc963801461038a578063936725ec1461039257806395d89b41146103b65780639d0bcca0146103be578063a0bcfc7f146103c657600080fd5b806335798ed01161018357806335798ed0146102b557806342842e0e146102bc57806342966c68146102cf5780634593144c146102e25780634f558e79146102ea5780634f6ccce7146102fd5780634fac6ccd14610310578063572b6c05146103235780636352211e1461033657600080fd5b806301ffc9a7146101ec57806306fdde0314610214578063081812fc14610229578063095ea7b31461024957806318160ddd1461025e57806323b872dd1461027457806323c137a5146102875780632f745c591461029a578063325a19f1146102ad575b600080fd5b6101ff6101fa36600461209c565b61049d565b60405190151581526020015b60405180910390f35b61021c6104c8565b60405161020b9190612106565b61023c610237366004612119565b610569565b60405161020b9190612132565b61025c61025736600461215b565b61057e565b005b610266610594565b60405190815260200161020b565b61025c610282366004612187565b6105a9565b61025c610295366004612282565b610630565b6102666102a836600461215b565b61069d565b6102666106ff565b60016101ff565b61025c6102ca366004612187565b610738565b61025c6102dd366004612119565b610758565b61026661080a565b6101ff6102f8366004612119565b61083a565b61026661030b366004612119565b610857565b61025c61031e3660046122d3565b6108b8565b6101ff6103313660046122d3565b6109bd565b61023c610344366004612119565b610a32565b61021c610a3d565b61026661035f3660046122d3565b610a47565b6102666103723660046122d3565b610a9f565b61025c610385366004612187565b610af8565b610266610b0e565b61021c604051806040016040528060058152602001640312e302e360dc1b81525081565b61021c610b2c565b61023c610b49565b61025c6103d43660046122f0565b610b79565b61025c6103e7366004612332565b610bc8565b61025c6103fa366004612360565b610bda565b6101ff61040d3660046122d3565b610c3a565b61025c6104203660046123a6565b610c5f565b61021c610433366004612119565b610c76565b6101ff6104463660046122d3565b610d87565b6101ff610459366004612425565b610e0c565b61023c610e4b565b61025c610474366004612453565b610e7b565b61021c604051806040016040528060058152602001640322e302e360dc1b81525081565b60006001600160e01b0319821663780e9d6360e01b14806104c257506104c282610f7b565b92915050565b606060006104d4610fcb565b90508060000180546104e5906124e1565b80601f0160208091040260200160405190810160405280929190818152602001828054610511906124e1565b801561055e5780601f106105335761010080835404028352916020019161055e565b820191906000526020600020905b81548152906001019060200180831161054157829003601f168201915b505050505091505090565b600061057482610fef565b506104c282611027565b610590828261058b611050565b611072565b5050565b60008061059f61107f565b6002015492915050565b6001600160a01b0382166105dc576000604051633250574960e11b81526004016105d39190612132565b60405180910390fd5b60006105f083836105eb611050565b6110a3565b9050836001600160a01b0316816001600160a01b03161461062a578382826040516364283d7b60e01b81526004016105d39392919061251b565b50505050565b6106386110b9565b8161064161114e565b60ff83166000908152602091909152604090209061065f908261258e565b507f29f443087b03c724ded11254c74c7211dcb5e505b280d10747d67f3a02278790828260405161069192919061264d565b60405180910390a15050565b6000806106a861107f565b90506106b384610a47565b83106106d657838360405163295f44f760e21b81526004016105d3929190612672565b6001600160a01b0384166000908152602091825260408082208583529092522054905092915050565b600061073361072f60017f6f55f470bdc9cb5f04223fd822021061668e4dccb43e8727b295106dc9769c8b6126a1565b5490565b905090565b61075383838360405180602001604052806000815250610c5f565b505050565b6107ca610763610e4b565b6001600160a01b03166331423c266040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107c491906126b4565b33611172565b6107d3816111a6565b6040518181527fc6eb113408f238574359334840f6fe1ced3001faf98cb0ae3fac7f64b5d61025906020015b60405180910390a150565b600061073361072f60017f812a673dfca07956350df10f8a654925f561d7a0da09bdbe79e653939a14d9f16126a1565b600080610846836111e1565b6001600160a01b0316141592915050565b60008061086261107f565b905061086c610594565b83106108905760008360405163295f44f760e21b81526004016105d3929190612672565b8060020183815481106108a5576108a56126d1565b9060005260206000200154915050919050565b3330146109055760405162461bcd60e51b815260206004820152601b60248201527a24b731b932b0b9b2903932bb34b9b4b7b7103337b93134b23232b760291b60448201526064016105d3565b600061092361072f60016000805160206128338339815191526126a1565b61092e9060016126e7565b90506109508161094d60016000805160206128338339815191526126a1565b55565b61097f8261094d60017fbfaaa2fb63266ff27c2da975f5894955056f50419af651a81f6c5060581857e46126a1565b604080518281526001600160a01b03841660208201527ff27e2ef832a4eb8ed8ec553b875eecd44764cda95b1c24170e281539e0a869c89101610691565b60006001600160a01b03821673d8253782c45a12053594b9deb72d8e8ab2fca54c1480610a0657506001600160a01b0382167352ceba41da235af367bfc0b0ccd3314cb901bb5f145b806104c257506001600160a01b03821673102f1f556cd9c3d5f820e6920a8931657c5da21b1492915050565b60006104c282610fef565b606061073361120a565b600080610a52610fcb565b90506001600160a01b038316610a7e5760006040516322718ad960e21b81526004016105d39190612132565b6001600160a01b039092166000908152600390920160205250604090205490565b6000610aac610763610e4b565b610ab46112a5565b9050610ac08282611300565b6040518181527fb024351dd495ddd57c50334b2a17ad2e929b28be0ea5a482e0e9b4277766c67b9060200160405180910390a1919050565b610b03610763610e4b565b61075383838361131a565b600061073361072f60016000805160206128338339815191526126a1565b60606000610b38610fcb565b90508060010180546104e5906124e1565b600061073361072f60017fbfaaa2fb63266ff27c2da975f5894955056f50419af651a81f6c5060581857e46126a1565b610b816110b9565b80610b8a611335565b60010190610b98908261258e565b507f87cdeaffd8e70903d6ce7cc983fac3b09ca79e83818124c98e47a1d70f8027d6816040516107ff9190612106565b610590610bd3611050565b8383611359565b610be26110b9565b80610beb611335565b60008481526002919091016020526040902090610c08908261258e565b507ff32b5bcd021361ac825fde09ff2121b0e5783f9f993196194fab9471799e54dd82826040516106919291906126fa565b6000610c44610e4b565b6001600160a01b0316826001600160a01b0316149050919050565b610c6a8484846105a9565b61062a848484846113fe565b6060610c80611335565b54821115610ca457604051636438665560e11b8152600481018390526024016105d3565b6000610cae611335565b6000848152600291909101602052604090208054610ccb906124e1565b80601f0160208091040260200160405190810160405280929190818152602001828054610cf7906124e1565b8015610d445780601f10610d1957610100808354040283529160200191610d44565b820191906000526020600020905b815481529060010190602001808311610d2757829003601f168201915b505050505090508051600014610d5a5792915050565b6000610d658461151c565b90508051600014610d77579392505050565b610d7f61120a565b949350505050565b6000816001600160a01b0316610d9b610e4b565b6001600160a01b0316635aa6e6756040518163ffffffff1660e01b8152600401602060405180830381865afa158015610dd8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dfc91906126b4565b6001600160a01b03161492915050565b600080610e17610fcb565b6001600160a01b03948516600090815260059190910160209081526040808320959096168252939093525050205460ff1690565b600061073361072f60017f5165972ef41194f06c5007493031d0b927c20741adcb74403b954009fd2c36186126a1565b6000610e856116bc565b805490915060ff600160401b82041615906001600160401b0316600081158015610eac5750825b90506000826001600160401b03166001148015610ec85750303b155b905081158015610ed6575080155b15610ef45760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610f1e57845460ff60401b1916600160401b1785555b610f2a88888b896116e0565b8315610f7057845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050565b60006001600160e01b031982166380ac58cd60e01b1480610fac57506001600160e01b03198216635b5e139f60e01b145b806104c257506301ffc9a760e01b6001600160e01b03198316146104c2565b7f80bb2b638cc20bc4d0a60d66940f3ab4a00c1d7b313497ca82fb0b4ab007930090565b600080610ffb836111e1565b90506001600160a01b0381166104c257604051637e27328960e01b8152600481018490526024016105d3565b600080611032610fcb565b6000938452600401602052505060409020546001600160a01b031690565b600061105b336109bd565b1561106d575060131936013560601c90565b503390565b61075383838360016116f4565b7f645e039705490088daad89bae25049a34f4a9072d398537b1ab2425f24cbed0090565b60006110ae836117fe565b610d7f8484846118f8565b6110c1610e4b565b6001600160a01b03166350c358a4336040518263ffffffff1660e01b81526004016110ec9190612132565b602060405180830381865afa158015611109573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112d9190612713565b61114c573360405163451cea1760e11b81526004016105d39190612132565b565b7f2dd41482e37d186fdf6545c563673785f2bcb485d6039f6c172d58b496a6e00090565b806001600160a01b0316826001600160a01b031614610590578060405163f6b9c4d960e01b81526004016105d39190612132565b60006111b560008360006110a3565b90506001600160a01b03811661059057604051637e27328960e01b8152600481018390526024016105d3565b6000806111ec610fcb565b6000938452600201602052505060409020546001600160a01b031690565b6060611214611335565b6001018054611222906124e1565b80601f016020809104026020016040519081016040528092919081815260200182805461124e906124e1565b801561129b5780601f106112705761010080835404028352916020019161129b565b820191906000526020600020905b81548152906001019060200180831161127e57829003601f168201915b5050505050905090565b6000806112b0611335565b5490506001600160401b036112c68260016126e7565b106112e7576040516308278bff60e41b8152600481018290526024016105d3565b6112f28160016126e7565b6112fa611335565b55919050565b610590828260405180602001604052806000815250611986565b6107538383836040518060200160405280600081525061199d565b7fda2932ada77a3c8131d8c171a8679090714572b6a41aff2e2186c297ac0f550090565b6000611363610fcb565b90506001600160a01b03831661138e5782604051630b61174360e31b81526004016105d39190612132565b6001600160a01b038481166000818152600584016020908152604080832094881680845294825291829020805460ff191687151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a350505050565b6001600160a01b0383163b1561062a57826001600160a01b031663150b7a02611425611050565b8685856040518563ffffffff1660e01b81526004016114479493929190612730565b6020604051808303816000875af1925050508015611482575060408051601f3d908101601f1916820190925261147f9181019061276d565b60015b6114e2573d8080156114b0576040519150601f19603f3d011682016040523d82523d6000602084013e6114b5565b606091505b5080516000036114da5783604051633250574960e11b81526004016105d39190612132565b805181602001fd5b6001600160e01b03198116630a85bd0160e11b146115155783604051633250574960e11b81526004016105d39190612132565b5050505050565b60606000611528610e4b565b6001600160a01b03166331423c266040518163ffffffff1660e01b8152600401602060405180830381865afa158015611565573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061158991906126b4565b905061159361114e565b604051631cdaab6760e01b81526000906001600160a01b03841690631cdaab67906115c49030908990600401612672565b606060405180830381865afa1580156115e1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611605919061278a565b51600681111561161757611617612806565b60ff1660ff1681526020019081526020016000208054611636906124e1565b80601f0160208091040260200160405190810160405280929190818152602001828054611662906124e1565b80156116af5780601f10611684576101008083540402835291602001916116af565b820191906000526020600020905b81548152906001019060200180831161169257829003601f168201915b5050505050915050919050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0090565b6116e86119a8565b61062a848484846119cd565b60006116fe610fcb565b9050818061171457506001600160a01b03831615155b156117cd57600061172485610fef565b90506001600160a01b038416158015906117505750836001600160a01b0316816001600160a01b031614155b801561176357506117618185610e0c565b155b15611783578360405163a9fbf51f60e01b81526004016105d39190612132565b82156117cb5784866001600160a01b0316826001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b600093845260040160205250506040902080546001600160a01b0319166001600160a01b0392909216919091179055565b611806610e4b565b6001600160a01b03166331423c266040518163ffffffff1660e01b8152600401602060405180830381865afa158015611843573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061186791906126b4565b6001600160a01b03166327238f1230836040518363ffffffff1660e01b8152600401611894929190612672565b602060405180830381865afa1580156118b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118d59190612713565b6118f55760405163678d7c4760e11b8152600481018290526024016105d3565b50565b600080611906858585611a40565b90506001600160a01b0381166119245761191f84611b47565b611947565b846001600160a01b0316816001600160a01b031614611947576119478185611b80565b6001600160a01b0385166119635761195e84611c21565b610d7f565b846001600160a01b0316816001600160a01b031614610d7f57610d7f8585611cea565b6119908383611d42565b61075360008484846113fe565b610c6a848484611da7565b6119b0611e43565b61114c57604051631afcd79f60e31b815260040160405180910390fd5b6119d78484611e5d565b6119e082611e6f565b60016119ea611335565b55806119f4611335565b60010190611a02908261258e565b507f87cdeaffd8e70903d6ce7cc983fac3b09ca79e83818124c98e47a1d70f8027d681604051611a329190612106565b60405180910390a150505050565b600080611a4b610fcb565b90506000611a58856111e1565b90506001600160a01b03841615611a7457611a74818587611e80565b6001600160a01b03811615611ab457611a916000866000806116f4565b6001600160a01b0381166000908152600383016020526040902080546000190190555b6001600160a01b03861615611ae5576001600160a01b03861660009081526003830160205260409020805460010190555b600085815260028301602052604080822080546001600160a01b0319166001600160a01b038a811691821790925591518893918516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a495945050505050565b6000611b5161107f565b600281018054600085815260039093016020908152604084208290556001820183559183529120019190915550565b6000611b8a61107f565b90506000611b9784610a47565b6000848152600184016020526040902054909150808214611bec576001600160a01b03851660009081526020848152604080832085845282528083205484845281842081905583526001860190915290208190555b50600092835260018201602090815260408085208590556001600160a01b039095168452918252838320908352905290812055565b6000611c2b61107f565b6002810154909150600090611c42906001906126a1565b6000848152600384016020526040812054600285018054939450909284908110611c6e57611c6e6126d1565b9060005260206000200154905080846002018381548110611c9157611c916126d1565b600091825260208083209091019290925582815260038601909152604080822084905586825281205560028401805480611ccd57611ccd61281c565b600190038181906000526020600020016000905590555050505050565b6000611cf461107f565b905060006001611d0385610a47565b611d0d91906126a1565b6001600160a01b0390941660009081526020838152604080832087845282528083208690559482526001909301909252502055565b6001600160a01b038216611d6c576000604051633250574960e11b81526004016105d39190612132565b6000611d7a838360006110a3565b90506001600160a01b038116156107535760006040516339e3563760e11b81526004016105d39190612132565b6001600160a01b038216611dd1576000604051633250574960e11b81526004016105d39190612132565b6000611ddf838360006110a3565b90506001600160a01b038116611e0b57604051637e27328960e01b8152600481018390526024016105d3565b836001600160a01b0316816001600160a01b03161461062a578382826040516364283d7b60e01b81526004016105d39392919061251b565b6000611e4d6116bc565b54600160401b900460ff16919050565b611e656119a8565b6105908282611ed6565b611e776119a8565b6118f581611f04565b611e8b838383612021565b610753576001600160a01b038316611eb957604051637e27328960e01b8152600481018290526024016105d3565b818160405163177e802f60e01b81526004016105d3929190612672565b611ede6119a8565b6000611ee8610fcb565b905080611ef5848261258e565b506001810161062a838261258e565b6001600160a01b038116611f4c5760405162461bcd60e51b815260206004820152600f60248201526e2d32b9379031b7b73a3937b63632b960891b60448201526064016105d3565b611f7b8161094d60017f5165972ef41194f06c5007493031d0b927c20741adcb74403b954009fd2c36186126a1565b611faa4261094d60017f6f55f470bdc9cb5f04223fd822021061668e4dccb43e8727b295106dc9769c8b6126a1565b611fd94361094d60017f812a673dfca07956350df10f8a654925f561d7a0da09bdbe79e653939a14d9f16126a1565b604080516001600160a01b038316815242602082015243918101919091527f1a2dd071001ebf6e03174e3df5b305795a4ad5d41d8fdb9ba41dbbe236713426906060016107ff565b60006001600160a01b03831615801590610d7f5750826001600160a01b0316846001600160a01b0316148061205b575061205b8484610e0c565b80610d7f5750826001600160a01b031661207483611027565b6001600160a01b031614949350505050565b6001600160e01b0319811681146118f557600080fd5b6000602082840312156120ae57600080fd5b81356120b981612086565b9392505050565b6000815180845260005b818110156120e6576020818501810151868301820152016120ca565b506000602082860101526020601f19601f83011685010191505092915050565b6020815260006120b960208301846120c0565b60006020828403121561212b57600080fd5b5035919050565b6001600160a01b0391909116815260200190565b6001600160a01b03811681146118f557600080fd5b6000806040838503121561216e57600080fd5b823561217981612146565b946020939093013593505050565b60008060006060848603121561219c57600080fd5b83356121a781612146565b925060208401356121b781612146565b929592945050506040919091013590565b634e487b7160e01b600052604160045260246000fd5b60006001600160401b03808411156121f8576121f86121c8565b604051601f8501601f19908116603f01168101908282118183101715612220576122206121c8565b8160405280935085815286868601111561223957600080fd5b858560208301376000602087830101525050509392505050565b600082601f83011261226457600080fd5b6120b9838335602085016121de565b60ff811681146118f557600080fd5b6000806040838503121561229557600080fd5b82356001600160401b038111156122ab57600080fd5b6122b785828601612253565b92505060208301356122c881612273565b809150509250929050565b6000602082840312156122e557600080fd5b81356120b981612146565b60006020828403121561230257600080fd5b81356001600160401b0381111561231857600080fd5b610d7f84828501612253565b80151581146118f557600080fd5b6000806040838503121561234557600080fd5b823561235081612146565b915060208301356122c881612324565b6000806040838503121561237357600080fd5b8235915060208301356001600160401b0381111561239057600080fd5b61239c85828601612253565b9150509250929050565b600080600080608085870312156123bc57600080fd5b84356123c781612146565b935060208501356123d781612146565b92506040850135915060608501356001600160401b038111156123f957600080fd5b8501601f8101871361240a57600080fd5b612419878235602084016121de565b91505092959194509250565b6000806040838503121561243857600080fd5b823561244381612146565b915060208301356122c881612146565b6000806000806080858703121561246957600080fd5b843561247481612146565b935060208501356001600160401b038082111561249057600080fd5b61249c88838901612253565b945060408701359150808211156124b257600080fd5b6124be88838901612253565b935060608701359150808211156124d457600080fd5b5061241987828801612253565b600181811c908216806124f557607f821691505b60208210810361251557634e487b7160e01b600052602260045260246000fd5b50919050565b6001600160a01b0393841681526020810192909252909116604082015260600190565b601f821115610753576000816000526020600020601f850160051c810160208610156125675750805b601f850160051c820191505b8181101561258657828155600101612573565b505050505050565b81516001600160401b038111156125a7576125a76121c8565b6125bb816125b584546124e1565b8461253e565b602080601f8311600181146125f057600084156125d85750858301515b600019600386901b1c1916600185901b178555612586565b600085815260208120601f198616915b8281101561261f57888601518255948401946001909101908401612600565b508582101561263d5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60408152600061266060408301856120c0565b905060ff831660208301529392505050565b6001600160a01b03929092168252602082015260400190565b634e487b7160e01b600052601160045260246000fd5b818103818111156104c2576104c261268b565b6000602082840312156126c657600080fd5b81516120b981612146565b634e487b7160e01b600052603260045260246000fd5b808201808211156104c2576104c261268b565b828152604060208201526000610d7f60408301846120c0565b60006020828403121561272557600080fd5b81516120b981612324565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090612763908301846120c0565b9695505050505050565b60006020828403121561277f57600080fd5b81516120b981612086565b60006060828403121561279c57600080fd5b604051606081018181106001600160401b03821117156127be576127be6121c8565b6040528251600781106127d057600080fd5b815260208301516127e081612273565b6020820152604083015161ffff811681146127fa57600080fd5b60408201529392505050565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052603160045260246000fdfe22573091f17911fb166032a3d9e0554aa73d31b7b7ddea4a4dd2995650af84bda2646970667358221220b170337cda0c295440a153ddc0f6b13e86dfe037708baa986fc6a149a0343a1d64736f6c63430008170033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101e75760003560e01c80636c0360eb11610110578063a22cb465116100a8578063a22cb465146103d9578063a60138a7146103ec578063b429afeb146103ff578063b88d4fde14610412578063c87b56dd14610425578063dee1f0e414610438578063e985e9c51461044b578063f77c47911461045e578063fcbdbaaa14610466578063ffa1ad741461047957600080fd5b80636c0360eb1461034957806370a082311461035157806371e578dc146103645780637746fa28146103775780637cc963801461038a578063936725ec1461039257806395d89b41146103b65780639d0bcca0146103be578063a0bcfc7f146103c657600080fd5b806335798ed01161018357806335798ed0146102b557806342842e0e146102bc57806342966c68146102cf5780634593144c146102e25780634f558e79146102ea5780634f6ccce7146102fd5780634fac6ccd14610310578063572b6c05146103235780636352211e1461033657600080fd5b806301ffc9a7146101ec57806306fdde0314610214578063081812fc14610229578063095ea7b31461024957806318160ddd1461025e57806323b872dd1461027457806323c137a5146102875780632f745c591461029a578063325a19f1146102ad575b600080fd5b6101ff6101fa36600461209c565b61049d565b60405190151581526020015b60405180910390f35b61021c6104c8565b60405161020b9190612106565b61023c610237366004612119565b610569565b60405161020b9190612132565b61025c61025736600461215b565b61057e565b005b610266610594565b60405190815260200161020b565b61025c610282366004612187565b6105a9565b61025c610295366004612282565b610630565b6102666102a836600461215b565b61069d565b6102666106ff565b60016101ff565b61025c6102ca366004612187565b610738565b61025c6102dd366004612119565b610758565b61026661080a565b6101ff6102f8366004612119565b61083a565b61026661030b366004612119565b610857565b61025c61031e3660046122d3565b6108b8565b6101ff6103313660046122d3565b6109bd565b61023c610344366004612119565b610a32565b61021c610a3d565b61026661035f3660046122d3565b610a47565b6102666103723660046122d3565b610a9f565b61025c610385366004612187565b610af8565b610266610b0e565b61021c604051806040016040528060058152602001640312e302e360dc1b81525081565b61021c610b2c565b61023c610b49565b61025c6103d43660046122f0565b610b79565b61025c6103e7366004612332565b610bc8565b61025c6103fa366004612360565b610bda565b6101ff61040d3660046122d3565b610c3a565b61025c6104203660046123a6565b610c5f565b61021c610433366004612119565b610c76565b6101ff6104463660046122d3565b610d87565b6101ff610459366004612425565b610e0c565b61023c610e4b565b61025c610474366004612453565b610e7b565b61021c604051806040016040528060058152602001640322e302e360dc1b81525081565b60006001600160e01b0319821663780e9d6360e01b14806104c257506104c282610f7b565b92915050565b606060006104d4610fcb565b90508060000180546104e5906124e1565b80601f0160208091040260200160405190810160405280929190818152602001828054610511906124e1565b801561055e5780601f106105335761010080835404028352916020019161055e565b820191906000526020600020905b81548152906001019060200180831161054157829003601f168201915b505050505091505090565b600061057482610fef565b506104c282611027565b610590828261058b611050565b611072565b5050565b60008061059f61107f565b6002015492915050565b6001600160a01b0382166105dc576000604051633250574960e11b81526004016105d39190612132565b60405180910390fd5b60006105f083836105eb611050565b6110a3565b9050836001600160a01b0316816001600160a01b03161461062a578382826040516364283d7b60e01b81526004016105d39392919061251b565b50505050565b6106386110b9565b8161064161114e565b60ff83166000908152602091909152604090209061065f908261258e565b507f29f443087b03c724ded11254c74c7211dcb5e505b280d10747d67f3a02278790828260405161069192919061264d565b60405180910390a15050565b6000806106a861107f565b90506106b384610a47565b83106106d657838360405163295f44f760e21b81526004016105d3929190612672565b6001600160a01b0384166000908152602091825260408082208583529092522054905092915050565b600061073361072f60017f6f55f470bdc9cb5f04223fd822021061668e4dccb43e8727b295106dc9769c8b6126a1565b5490565b905090565b61075383838360405180602001604052806000815250610c5f565b505050565b6107ca610763610e4b565b6001600160a01b03166331423c266040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107c491906126b4565b33611172565b6107d3816111a6565b6040518181527fc6eb113408f238574359334840f6fe1ced3001faf98cb0ae3fac7f64b5d61025906020015b60405180910390a150565b600061073361072f60017f812a673dfca07956350df10f8a654925f561d7a0da09bdbe79e653939a14d9f16126a1565b600080610846836111e1565b6001600160a01b0316141592915050565b60008061086261107f565b905061086c610594565b83106108905760008360405163295f44f760e21b81526004016105d3929190612672565b8060020183815481106108a5576108a56126d1565b9060005260206000200154915050919050565b3330146109055760405162461bcd60e51b815260206004820152601b60248201527a24b731b932b0b9b2903932bb34b9b4b7b7103337b93134b23232b760291b60448201526064016105d3565b600061092361072f60016000805160206128338339815191526126a1565b61092e9060016126e7565b90506109508161094d60016000805160206128338339815191526126a1565b55565b61097f8261094d60017fbfaaa2fb63266ff27c2da975f5894955056f50419af651a81f6c5060581857e46126a1565b604080518281526001600160a01b03841660208201527ff27e2ef832a4eb8ed8ec553b875eecd44764cda95b1c24170e281539e0a869c89101610691565b60006001600160a01b03821673d8253782c45a12053594b9deb72d8e8ab2fca54c1480610a0657506001600160a01b0382167352ceba41da235af367bfc0b0ccd3314cb901bb5f145b806104c257506001600160a01b03821673102f1f556cd9c3d5f820e6920a8931657c5da21b1492915050565b60006104c282610fef565b606061073361120a565b600080610a52610fcb565b90506001600160a01b038316610a7e5760006040516322718ad960e21b81526004016105d39190612132565b6001600160a01b039092166000908152600390920160205250604090205490565b6000610aac610763610e4b565b610ab46112a5565b9050610ac08282611300565b6040518181527fb024351dd495ddd57c50334b2a17ad2e929b28be0ea5a482e0e9b4277766c67b9060200160405180910390a1919050565b610b03610763610e4b565b61075383838361131a565b600061073361072f60016000805160206128338339815191526126a1565b60606000610b38610fcb565b90508060010180546104e5906124e1565b600061073361072f60017fbfaaa2fb63266ff27c2da975f5894955056f50419af651a81f6c5060581857e46126a1565b610b816110b9565b80610b8a611335565b60010190610b98908261258e565b507f87cdeaffd8e70903d6ce7cc983fac3b09ca79e83818124c98e47a1d70f8027d6816040516107ff9190612106565b610590610bd3611050565b8383611359565b610be26110b9565b80610beb611335565b60008481526002919091016020526040902090610c08908261258e565b507ff32b5bcd021361ac825fde09ff2121b0e5783f9f993196194fab9471799e54dd82826040516106919291906126fa565b6000610c44610e4b565b6001600160a01b0316826001600160a01b0316149050919050565b610c6a8484846105a9565b61062a848484846113fe565b6060610c80611335565b54821115610ca457604051636438665560e11b8152600481018390526024016105d3565b6000610cae611335565b6000848152600291909101602052604090208054610ccb906124e1565b80601f0160208091040260200160405190810160405280929190818152602001828054610cf7906124e1565b8015610d445780601f10610d1957610100808354040283529160200191610d44565b820191906000526020600020905b815481529060010190602001808311610d2757829003601f168201915b505050505090508051600014610d5a5792915050565b6000610d658461151c565b90508051600014610d77579392505050565b610d7f61120a565b949350505050565b6000816001600160a01b0316610d9b610e4b565b6001600160a01b0316635aa6e6756040518163ffffffff1660e01b8152600401602060405180830381865afa158015610dd8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dfc91906126b4565b6001600160a01b03161492915050565b600080610e17610fcb565b6001600160a01b03948516600090815260059190910160209081526040808320959096168252939093525050205460ff1690565b600061073361072f60017f5165972ef41194f06c5007493031d0b927c20741adcb74403b954009fd2c36186126a1565b6000610e856116bc565b805490915060ff600160401b82041615906001600160401b0316600081158015610eac5750825b90506000826001600160401b03166001148015610ec85750303b155b905081158015610ed6575080155b15610ef45760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610f1e57845460ff60401b1916600160401b1785555b610f2a88888b896116e0565b8315610f7057845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050565b60006001600160e01b031982166380ac58cd60e01b1480610fac57506001600160e01b03198216635b5e139f60e01b145b806104c257506301ffc9a760e01b6001600160e01b03198316146104c2565b7f80bb2b638cc20bc4d0a60d66940f3ab4a00c1d7b313497ca82fb0b4ab007930090565b600080610ffb836111e1565b90506001600160a01b0381166104c257604051637e27328960e01b8152600481018490526024016105d3565b600080611032610fcb565b6000938452600401602052505060409020546001600160a01b031690565b600061105b336109bd565b1561106d575060131936013560601c90565b503390565b61075383838360016116f4565b7f645e039705490088daad89bae25049a34f4a9072d398537b1ab2425f24cbed0090565b60006110ae836117fe565b610d7f8484846118f8565b6110c1610e4b565b6001600160a01b03166350c358a4336040518263ffffffff1660e01b81526004016110ec9190612132565b602060405180830381865afa158015611109573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112d9190612713565b61114c573360405163451cea1760e11b81526004016105d39190612132565b565b7f2dd41482e37d186fdf6545c563673785f2bcb485d6039f6c172d58b496a6e00090565b806001600160a01b0316826001600160a01b031614610590578060405163f6b9c4d960e01b81526004016105d39190612132565b60006111b560008360006110a3565b90506001600160a01b03811661059057604051637e27328960e01b8152600481018390526024016105d3565b6000806111ec610fcb565b6000938452600201602052505060409020546001600160a01b031690565b6060611214611335565b6001018054611222906124e1565b80601f016020809104026020016040519081016040528092919081815260200182805461124e906124e1565b801561129b5780601f106112705761010080835404028352916020019161129b565b820191906000526020600020905b81548152906001019060200180831161127e57829003601f168201915b5050505050905090565b6000806112b0611335565b5490506001600160401b036112c68260016126e7565b106112e7576040516308278bff60e41b8152600481018290526024016105d3565b6112f28160016126e7565b6112fa611335565b55919050565b610590828260405180602001604052806000815250611986565b6107538383836040518060200160405280600081525061199d565b7fda2932ada77a3c8131d8c171a8679090714572b6a41aff2e2186c297ac0f550090565b6000611363610fcb565b90506001600160a01b03831661138e5782604051630b61174360e31b81526004016105d39190612132565b6001600160a01b038481166000818152600584016020908152604080832094881680845294825291829020805460ff191687151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a350505050565b6001600160a01b0383163b1561062a57826001600160a01b031663150b7a02611425611050565b8685856040518563ffffffff1660e01b81526004016114479493929190612730565b6020604051808303816000875af1925050508015611482575060408051601f3d908101601f1916820190925261147f9181019061276d565b60015b6114e2573d8080156114b0576040519150601f19603f3d011682016040523d82523d6000602084013e6114b5565b606091505b5080516000036114da5783604051633250574960e11b81526004016105d39190612132565b805181602001fd5b6001600160e01b03198116630a85bd0160e11b146115155783604051633250574960e11b81526004016105d39190612132565b5050505050565b60606000611528610e4b565b6001600160a01b03166331423c266040518163ffffffff1660e01b8152600401602060405180830381865afa158015611565573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061158991906126b4565b905061159361114e565b604051631cdaab6760e01b81526000906001600160a01b03841690631cdaab67906115c49030908990600401612672565b606060405180830381865afa1580156115e1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611605919061278a565b51600681111561161757611617612806565b60ff1660ff1681526020019081526020016000208054611636906124e1565b80601f0160208091040260200160405190810160405280929190818152602001828054611662906124e1565b80156116af5780601f10611684576101008083540402835291602001916116af565b820191906000526020600020905b81548152906001019060200180831161169257829003601f168201915b5050505050915050919050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0090565b6116e86119a8565b61062a848484846119cd565b60006116fe610fcb565b9050818061171457506001600160a01b03831615155b156117cd57600061172485610fef565b90506001600160a01b038416158015906117505750836001600160a01b0316816001600160a01b031614155b801561176357506117618185610e0c565b155b15611783578360405163a9fbf51f60e01b81526004016105d39190612132565b82156117cb5784866001600160a01b0316826001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b600093845260040160205250506040902080546001600160a01b0319166001600160a01b0392909216919091179055565b611806610e4b565b6001600160a01b03166331423c266040518163ffffffff1660e01b8152600401602060405180830381865afa158015611843573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061186791906126b4565b6001600160a01b03166327238f1230836040518363ffffffff1660e01b8152600401611894929190612672565b602060405180830381865afa1580156118b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118d59190612713565b6118f55760405163678d7c4760e11b8152600481018290526024016105d3565b50565b600080611906858585611a40565b90506001600160a01b0381166119245761191f84611b47565b611947565b846001600160a01b0316816001600160a01b031614611947576119478185611b80565b6001600160a01b0385166119635761195e84611c21565b610d7f565b846001600160a01b0316816001600160a01b031614610d7f57610d7f8585611cea565b6119908383611d42565b61075360008484846113fe565b610c6a848484611da7565b6119b0611e43565b61114c57604051631afcd79f60e31b815260040160405180910390fd5b6119d78484611e5d565b6119e082611e6f565b60016119ea611335565b55806119f4611335565b60010190611a02908261258e565b507f87cdeaffd8e70903d6ce7cc983fac3b09ca79e83818124c98e47a1d70f8027d681604051611a329190612106565b60405180910390a150505050565b600080611a4b610fcb565b90506000611a58856111e1565b90506001600160a01b03841615611a7457611a74818587611e80565b6001600160a01b03811615611ab457611a916000866000806116f4565b6001600160a01b0381166000908152600383016020526040902080546000190190555b6001600160a01b03861615611ae5576001600160a01b03861660009081526003830160205260409020805460010190555b600085815260028301602052604080822080546001600160a01b0319166001600160a01b038a811691821790925591518893918516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a495945050505050565b6000611b5161107f565b600281018054600085815260039093016020908152604084208290556001820183559183529120019190915550565b6000611b8a61107f565b90506000611b9784610a47565b6000848152600184016020526040902054909150808214611bec576001600160a01b03851660009081526020848152604080832085845282528083205484845281842081905583526001860190915290208190555b50600092835260018201602090815260408085208590556001600160a01b039095168452918252838320908352905290812055565b6000611c2b61107f565b6002810154909150600090611c42906001906126a1565b6000848152600384016020526040812054600285018054939450909284908110611c6e57611c6e6126d1565b9060005260206000200154905080846002018381548110611c9157611c916126d1565b600091825260208083209091019290925582815260038601909152604080822084905586825281205560028401805480611ccd57611ccd61281c565b600190038181906000526020600020016000905590555050505050565b6000611cf461107f565b905060006001611d0385610a47565b611d0d91906126a1565b6001600160a01b0390941660009081526020838152604080832087845282528083208690559482526001909301909252502055565b6001600160a01b038216611d6c576000604051633250574960e11b81526004016105d39190612132565b6000611d7a838360006110a3565b90506001600160a01b038116156107535760006040516339e3563760e11b81526004016105d39190612132565b6001600160a01b038216611dd1576000604051633250574960e11b81526004016105d39190612132565b6000611ddf838360006110a3565b90506001600160a01b038116611e0b57604051637e27328960e01b8152600481018390526024016105d3565b836001600160a01b0316816001600160a01b03161461062a578382826040516364283d7b60e01b81526004016105d39392919061251b565b6000611e4d6116bc565b54600160401b900460ff16919050565b611e656119a8565b6105908282611ed6565b611e776119a8565b6118f581611f04565b611e8b838383612021565b610753576001600160a01b038316611eb957604051637e27328960e01b8152600481018290526024016105d3565b818160405163177e802f60e01b81526004016105d3929190612672565b611ede6119a8565b6000611ee8610fcb565b905080611ef5848261258e565b506001810161062a838261258e565b6001600160a01b038116611f4c5760405162461bcd60e51b815260206004820152600f60248201526e2d32b9379031b7b73a3937b63632b960891b60448201526064016105d3565b611f7b8161094d60017f5165972ef41194f06c5007493031d0b927c20741adcb74403b954009fd2c36186126a1565b611faa4261094d60017f6f55f470bdc9cb5f04223fd822021061668e4dccb43e8727b295106dc9769c8b6126a1565b611fd94361094d60017f812a673dfca07956350df10f8a654925f561d7a0da09bdbe79e653939a14d9f16126a1565b604080516001600160a01b038316815242602082015243918101919091527f1a2dd071001ebf6e03174e3df5b305795a4ad5d41d8fdb9ba41dbbe236713426906060016107ff565b60006001600160a01b03831615801590610d7f5750826001600160a01b0316846001600160a01b0316148061205b575061205b8484610e0c565b80610d7f5750826001600160a01b031661207483611027565b6001600160a01b031614949350505050565b6001600160e01b0319811681146118f557600080fd5b6000602082840312156120ae57600080fd5b81356120b981612086565b9392505050565b6000815180845260005b818110156120e6576020818501810151868301820152016120ca565b506000602082860101526020601f19601f83011685010191505092915050565b6020815260006120b960208301846120c0565b60006020828403121561212b57600080fd5b5035919050565b6001600160a01b0391909116815260200190565b6001600160a01b03811681146118f557600080fd5b6000806040838503121561216e57600080fd5b823561217981612146565b946020939093013593505050565b60008060006060848603121561219c57600080fd5b83356121a781612146565b925060208401356121b781612146565b929592945050506040919091013590565b634e487b7160e01b600052604160045260246000fd5b60006001600160401b03808411156121f8576121f86121c8565b604051601f8501601f19908116603f01168101908282118183101715612220576122206121c8565b8160405280935085815286868601111561223957600080fd5b858560208301376000602087830101525050509392505050565b600082601f83011261226457600080fd5b6120b9838335602085016121de565b60ff811681146118f557600080fd5b6000806040838503121561229557600080fd5b82356001600160401b038111156122ab57600080fd5b6122b785828601612253565b92505060208301356122c881612273565b809150509250929050565b6000602082840312156122e557600080fd5b81356120b981612146565b60006020828403121561230257600080fd5b81356001600160401b0381111561231857600080fd5b610d7f84828501612253565b80151581146118f557600080fd5b6000806040838503121561234557600080fd5b823561235081612146565b915060208301356122c881612324565b6000806040838503121561237357600080fd5b8235915060208301356001600160401b0381111561239057600080fd5b61239c85828601612253565b9150509250929050565b600080600080608085870312156123bc57600080fd5b84356123c781612146565b935060208501356123d781612146565b92506040850135915060608501356001600160401b038111156123f957600080fd5b8501601f8101871361240a57600080fd5b612419878235602084016121de565b91505092959194509250565b6000806040838503121561243857600080fd5b823561244381612146565b915060208301356122c881612146565b6000806000806080858703121561246957600080fd5b843561247481612146565b935060208501356001600160401b038082111561249057600080fd5b61249c88838901612253565b945060408701359150808211156124b257600080fd5b6124be88838901612253565b935060608701359150808211156124d457600080fd5b5061241987828801612253565b600181811c908216806124f557607f821691505b60208210810361251557634e487b7160e01b600052602260045260246000fd5b50919050565b6001600160a01b0393841681526020810192909252909116604082015260600190565b601f821115610753576000816000526020600020601f850160051c810160208610156125675750805b601f850160051c820191505b8181101561258657828155600101612573565b505050505050565b81516001600160401b038111156125a7576125a76121c8565b6125bb816125b584546124e1565b8461253e565b602080601f8311600181146125f057600084156125d85750858301515b600019600386901b1c1916600185901b178555612586565b600085815260208120601f198616915b8281101561261f57888601518255948401946001909101908401612600565b508582101561263d5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60408152600061266060408301856120c0565b905060ff831660208301529392505050565b6001600160a01b03929092168252602082015260400190565b634e487b7160e01b600052601160045260246000fd5b818103818111156104c2576104c261268b565b6000602082840312156126c657600080fd5b81516120b981612146565b634e487b7160e01b600052603260045260246000fd5b808201808211156104c2576104c261268b565b828152604060208201526000610d7f60408301846120c0565b60006020828403121561272557600080fd5b81516120b981612324565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090612763908301846120c0565b9695505050505050565b60006020828403121561277f57600080fd5b81516120b981612086565b60006060828403121561279c57600080fd5b604051606081018181106001600160401b03821117156127be576127be6121c8565b6040528251600781106127d057600080fd5b815260208301516127e081612273565b6020820152604083015161ffff811681146127fa57600080fd5b60408201529392505050565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052603160045260246000fdfe22573091f17911fb166032a3d9e0554aa73d31b7b7ddea4a4dd2995650af84bda2646970667358221220b170337cda0c295440a153ddc0f6b13e86dfe037708baa986fc6a149a0343a1d64736f6c63430008170033

Block Transaction 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

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.