Overview
S Balance
S Value
$0.00More Info
Private Name Tags
ContractCreator
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
StorySetupLib
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 50 runs
Other Settings:
istanbul EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.23; import "../interfaces/IStoryController.sol"; import "../interfaces/IAppErrors.sol"; import "../interfaces/IApplicationEvents.sol"; import "../lib/PackingLib.sol"; import "../lib/StatLib.sol"; library StorySetupLib { using EnumerableSet for EnumerableSet.UintSet; using EnumerableSet for EnumerableSet.Bytes32Set; using PackingLib for bytes32; using PackingLib for uint16; using PackingLib for uint8; using PackingLib for address; using PackingLib for uint32[]; using PackingLib for uint32; using PackingLib for uint64; using PackingLib for int32[]; using PackingLib for int32; //region ------------------ Data types struct RemoveStoryContext { uint8 heroClass; uint8 answerResultId; uint8 customDataResultId; uint16 storyId; uint16 pageId; uint16 answerNum; uint len; uint[] tmpPages; bytes32 answerId; bytes32[] tmpAnswers; } //endregion ------------------ Data types //region ------------------ Set story fields function setAllStoryFields(IStoryController.MainState storage s, IStoryController.StoryMetaInfo memory meta) external { setBurnItemsMeta(s, meta.storyId, meta.answerBurnRandomItemMeta); setNextObjRewriteMeta(s, meta.storyId, meta.nextObjRewriteMeta); setAnswersMeta( s, meta.storyId, meta.answersMeta.answerPageIds, meta.answersMeta.answerHeroClasses, meta.answersMeta.answerIds ); setAnswerNextPageMeta(s, meta.storyId, meta.answerNextPage); setAnswerAttributeRequirements(s, meta.storyId, meta.answerAttributeRequirements); setAnswerItemRequirements(s, meta.storyId, meta.answerItemRequirements); setAnswerTokenRequirementsMeta(s, meta.storyId, meta.answerTokenRequirements); setAnswerAttributes(s, meta.storyId, meta.answerAttributes); setAnswerHeroCustomDataRequirementMeta(s, meta.storyId, meta.answerHeroCustomDataRequirement); setAnswerGlobalCustomDataRequirementMeta(s, meta.storyId, meta.answerGlobalCustomDataRequirement); setSuccessInfo(s, meta.storyId, meta.successInfo); setFailInfo(s, meta.storyId, meta.failInfo); setCustomDataResult(s, meta.storyId, meta.successHeroCustomData, IStoryController.CustomDataResult.HERO_SUCCESS); setCustomDataResult(s, meta.storyId, meta.failHeroCustomData, IStoryController.CustomDataResult.HERO_FAIL); setCustomDataResult(s, meta.storyId, meta.successGlobalCustomData, IStoryController.CustomDataResult.GLOBAL_SUCCESS); setCustomDataResult(s, meta.storyId, meta.failGlobalCustomData, IStoryController.CustomDataResult.GLOBAL_FAIL); setStoryCustomDataRequirements( s, meta.storyId, meta.requiredCustomDataIndex, meta.requiredCustomDataMinValue, meta.requiredCustomDataMaxValue, meta.requiredCustomDataIsHero, meta.minLevel ); } /// @dev Since SIP-003 the burning is replaced by breaking function setBurnItemsMeta( IStoryController.MainState storage s, uint16 storyId, IStoryController.AnswerBurnRandomItemMeta memory meta ) public { unchecked { uint len = meta.pageId.length; for (uint i; i < len; ++i) { bytes32 answerPackedId = _registerAnswer(s, storyId, meta.pageId[i], meta.heroClass[i], meta.answerId[i]); if (answerPackedId != bytes32(0)) { bytes32[] storage answersBurn = s.burnItem[answerPackedId]; for (uint j; j < meta.slots[i].length; ++j) { bytes32 d = meta.slots[i][j].packBreakInfo(meta.chances[i][j], meta.isStopIfBurnt[i][j]); if (d != bytes32(0)) { answersBurn.push(d); } } } } } emit IApplicationEvents.SetBurnItemsMeta(storyId, meta); } function setNextObjRewriteMeta(IStoryController.MainState storage s, uint16 storyId, IStoryController.NextObjRewriteMeta memory meta) public { unchecked { uint len = meta.nextObjPageIds.length; for (uint i; i < len; ++i) { registerPage(s, storyId, meta.nextObjPageIds[i]); bytes32 id = storyId.packStoryPageId(meta.nextObjPageIds[i], meta.nextObjHeroClasses[i]); s.nextObjectsRewrite[id] = meta.nextObjIds[i]; } } emit IApplicationEvents.SetNextObjRewriteMeta(storyId, meta); } function setAnswersMeta( IStoryController.MainState storage s, uint16 storyId, uint16[] memory answerPageIds, uint8[] memory answerHeroClasses, uint16[] memory answerIds ) public { unchecked { uint len = answerPageIds.length; for (uint i; i < len; ++i) { registerPage(s, storyId, answerPageIds[i]); bytes32[] storage answersHashes = s.answers[storyId.packStoryPageId(answerPageIds[i], answerHeroClasses[i])]; bytes32 answerPackedId = _registerAnswer(s, storyId, answerPageIds[i], answerHeroClasses[i], answerIds[i]); if (answerPackedId != bytes32(0)) { answersHashes.push(answerPackedId); } } } emit IApplicationEvents.SetAnswersMeta(storyId, answerPageIds, answerHeroClasses, answerIds); } function setAnswerNextPageMeta(IStoryController.MainState storage s, uint16 storyId, IStoryController.AnswerNextPageMeta memory meta) public { unchecked { uint len = meta.pageId.length; for (uint i; i < len; ++i) { bytes32 answerPackedId = _registerAnswer(s, storyId, meta.pageId[i], meta.heroClass[i], meta.answerId[i]); if (answerPackedId != bytes32(0)) { bytes32 pagePackedId = storyId.packStoryNextPagesId( meta.pageId[i], meta.heroClass[i], meta.answerId[i], meta.answerResultIds[i] ); // pagePackedId cannot be 0 here because answerPackedId is not 0 s.nextPageIds[pagePackedId] = meta.answerNextPageIds[i]; } } } emit IApplicationEvents.SetAnswerNextPageMeta(storyId, meta); } function setAnswerAttributeRequirements(IStoryController.MainState storage s, uint16 storyId, IStoryController.AnswerAttributeRequirementsMeta memory meta) public { unchecked { uint len = meta.pageId.length; for (uint i; i < len; ++i) { bytes32 answerPackedId = _registerAnswer(s, storyId, meta.pageId[i], meta.heroClass[i], meta.answerId[i]); if (answerPackedId != bytes32(0)) { bytes32[] storage attrs = s.attributeRequirements[answerPackedId]; for (uint j; j < meta.cores[i].length; ++j) { bytes32 attributeRequirementsPacked = meta.ids[i][j].packStoryAttributeRequirement( meta.values[i][j], meta.cores[i][j] ); if (attributeRequirementsPacked != bytes32(0)) { attrs.push(attributeRequirementsPacked); } } } } } emit IApplicationEvents.SetAnswerAttributeRequirements(storyId, meta); } function setAnswerItemRequirements(IStoryController.MainState storage s, uint16 storyId, IStoryController.AnswerItemRequirementsMeta memory meta) public { unchecked { uint len = meta.pageId.length; for (uint i; i < len; ++i) { bytes32 answerPackedId = _registerAnswer(s, storyId, meta.pageId[i], meta.heroClass[i], meta.answerId[i]); if (answerPackedId != bytes32(0)) { bytes32[] storage attrs = s.itemRequirements[answerPackedId]; for (uint j; j < meta.requireItems[i].length; ++j) { bytes32 d = meta.requireItems[i][j].packStoryItemRequirement( meta.requireItemBurn[i][j], meta.requireItemEquipped[i][j]); if (d != bytes32(0)) { attrs.push(d); } } } } } emit IApplicationEvents.SetAnswerItemRequirements(storyId, meta); } function setAnswerTokenRequirementsMeta(IStoryController.MainState storage s, uint16 storyId, IStoryController.AnswerTokenRequirementsMeta memory meta) public { unchecked { uint len = meta.pageId.length; for (uint i; i < len; ++i) { bytes32 answerPackedId = _registerAnswer(s, storyId, meta.pageId[i], meta.heroClass[i], meta.answerId[i]); if (answerPackedId != bytes32(0)) { bytes32[] storage attrs = s.tokenRequirements[answerPackedId]; for (uint j; j < meta.requireToken[i].length; ++j) { bytes32 d = meta.requireToken[i][j].packStoryTokenRequirement( meta.requireAmount[i][j], meta.requireTransfer[i][j] ); if (d != bytes32(0)) { attrs.push(d); } } } } } emit IApplicationEvents.SetAnswerTokenRequirementsMeta(storyId, meta); } function setAnswerAttributes(IStoryController.MainState storage s, uint16 storyId, IStoryController.AnswerAttributesMeta memory meta) public { unchecked { uint len = meta.pageId.length; for (uint i; i < len; ++i) { bytes32 answerPackedId = _registerAnswer(s, storyId, meta.pageId[i], meta.heroClass[i], meta.answerId[i]); if (answerPackedId != bytes32(0)) { bytes32 data = meta.randomRequirements[i].packStorySimpleRequirement( meta.delayRequirements[i], meta.isFinalAnswer[i] ); if (data != bytes32(0)) { s.answerAttributes[answerPackedId] = data; } } } } emit IApplicationEvents.SetAnswerAttributes(storyId, meta); } function setAnswerHeroCustomDataRequirementMeta(IStoryController.MainState storage s, uint16 storyId, IStoryController.AnswerCustomDataMeta memory meta) public { _setCustomDataRequirementMeta(s, storyId, meta, s.heroCustomDataRequirement); emit IApplicationEvents.SetAnswerHeroCustomDataRequirementMeta(storyId, meta); } function setAnswerGlobalCustomDataRequirementMeta(IStoryController.MainState storage s, uint16 storyId, IStoryController.AnswerCustomDataMeta memory meta) public { _setCustomDataRequirementMeta(s, storyId, meta, s.globalCustomDataRequirement); emit IApplicationEvents.SetAnswerGlobalCustomDataRequirementMeta(storyId, meta); } function setStoryCustomDataRequirements( IStoryController.MainState storage s, uint16 storyId, bytes32[] memory requiredCustomDataIndex, uint64[] memory requiredCustomDataMinValue, uint64[] memory requiredCustomDataMaxValue, bool[] memory requiredCustomDataIsHero, uint minLevel ) public { s.storyRequiredLevel[storyId] = minLevel; emit IApplicationEvents.StoryRequiredLevel(storyId, minLevel); IStoryController.CustomDataRequirementRangePacked[] storage allData = s.storyRequiredHeroData[storyId]; for (uint i; i < requiredCustomDataIndex.length; ++i) { allData.push(IStoryController.CustomDataRequirementRangePacked({ index: requiredCustomDataIndex[i], data: requiredCustomDataMinValue[i].packCustomDataRequirements( requiredCustomDataMaxValue[i], requiredCustomDataIsHero[i] ) })); emit IApplicationEvents.StoryCustomDataRequirements(storyId, requiredCustomDataIndex[i], requiredCustomDataMinValue[i], requiredCustomDataMaxValue[i], requiredCustomDataIsHero[i]); } } function setSuccessInfo(IStoryController.MainState storage s, uint16 storyId, IStoryController.AnswerResultMeta memory meta) public { _setInfo(s, storyId, meta, s.successInfoAttributes, s.successInfoStats, s.successInfoMintItems); emit IApplicationEvents.SetSuccessInfo(storyId, meta); } function setFailInfo(IStoryController.MainState storage s, uint16 storyId, IStoryController.AnswerResultMeta memory meta) public { _setInfo(s, storyId, meta, s.failInfoAttributes, s.failInfoStats, s.failInfoMintItems); emit IApplicationEvents.SetFailInfo(storyId, meta); } function setCustomDataResult( IStoryController.MainState storage s, uint16 storyId, IStoryController.AnswerCustomDataResultMeta memory meta, IStoryController.CustomDataResult type_ ) public { unchecked { uint len = meta.pageId.length; for (uint i; i < len; ++i) { if (_registerAnswer(s, storyId, meta.pageId[i], meta.heroClass[i], meta.answerId[i]) != bytes32(0)) { bytes32 answerPackedIdWithType = storyId.packStoryCustomDataResult( meta.pageId[i], meta.heroClass[i], meta.answerId[i], uint8(type_) ); bytes32[] storage arr = s.customDataResult[answerPackedIdWithType]; for (uint j; j < meta.dataIndexes[i].length; ++j) { arr.push(meta.dataIndexes[i][j].packCustomDataChange(meta.dataValues[i][j])); } } } } emit IApplicationEvents.SetCustomDataResult(storyId, meta, type_); } function finalizeStoryRegistration( IStoryController.MainState storage s, uint16 storyId, uint32 objectId, uint buildHash ) external { // it's not necessary to remove previously stored data here // we assume, that old data is already removed completely before registering new data s.registeredStories[objectId] = true; // store new used id s._usedStoryIds[storyId] = true; // register new id for story s.storyIds[objectId] = storyId; s.idToStory[storyId] = objectId; s.storyBuildHash[storyId] = buildHash; emit IApplicationEvents.StoryFinalized(objectId, storyId); } //endregion ------------------ Set story fields //region ------------------ Utils to set story fields function _registerAnswer(IStoryController.MainState storage s, uint16 storyId, uint16 pageId, uint8 heroClass, uint16 answerId) internal returns (bytes32 answerPackedId) { answerPackedId = storyId.packStoryAnswerId(pageId, heroClass, answerId); if (answerPackedId != bytes32(0)) { registerAnswer(s, storyId, answerPackedId); } } /// @param map Either heroCustomDataRequirement or globalCustomDataRequirement function _setCustomDataRequirementMeta( IStoryController.MainState storage s, uint16 storyId, IStoryController.AnswerCustomDataMeta memory meta, mapping(bytes32 => IStoryController.CustomDataRequirementPacked[]) storage map ) internal { unchecked { uint len = meta.pageId.length; for (uint i; i < len; ++i) { bytes32 answerPackedId = _registerAnswer(s, storyId, meta.pageId[i], meta.heroClass[i], meta.answerId[i]); if (answerPackedId != bytes32(0)) { IStoryController.CustomDataRequirementPacked[] storage arr = map[answerPackedId]; bytes32[] memory dataIndexes = meta.dataIndexes[i]; bool[] memory mandatory = meta.mandatory[i]; uint64[] memory dataValuesMin = meta.dataValuesMin[i]; uint64[] memory dataValuesMax = meta.dataValuesMax[i]; for (uint j; j < dataIndexes.length; ++j) { arr.push( IStoryController.CustomDataRequirementPacked({ index: dataIndexes[j], data: dataValuesMin[j].packCustomDataRequirements(dataValuesMax[j], mandatory[j]) }) ); } } } } } function _setInfo( IStoryController.MainState storage s, uint16 storyId, IStoryController.AnswerResultMeta memory meta, mapping(bytes32 => bytes32[]) storage infoAttributes, mapping(bytes32 => bytes32) storage infoStats, mapping(bytes32 => bytes32[]) storage infoMintItems ) public { unchecked { uint len = meta.pageId.length; for (uint i; i < len; ++i) { bytes32 answerPackedId = _registerAnswer(s, storyId, meta.pageId[i], meta.heroClass[i], meta.answerId[i]); if (answerPackedId != bytes32(0)) { if (meta.attributeIds[i].length != 0) { infoAttributes[answerPackedId] = meta.attributeValues[i].toBytes32ArrayWithIds(meta.attributeIds[i]); } bytes32 stats = PackingLib.packStatsChange( meta.experience[i], meta.heal[i], meta.manaRegen[i], meta.lifeChancesRecovered[i], meta.damage[i], meta.manaConsumed[i] ); if (stats != bytes32(0)) { infoStats[answerPackedId] = stats; } uint lenItems = meta.mintItems[i].length; if (lenItems != 0) { bytes32[] memory items = new bytes32[](lenItems); for (uint j; j < lenItems; ++j) { items[j] = meta.mintItems[i][j].packItemMintInfo(meta.mintItemsChances[i][j]); } infoMintItems[answerPackedId] = items; } } } } } //endregion ------------------ Utils to set story fields //region ------------------ Remove logic // WE MUST REMOVE ALL EXIST META! // otherwise we will still have meta for story id and will totally mess data function removeStory(IStoryController.MainState storage s, uint32 objectId) external { if (s.storyIds[objectId] == 0 || !s.registeredStories[objectId]) revert IAppErrors.ZeroStoryIdRemoveStory(); uint16 storyId = s.storyIds[objectId]; delete s._usedStoryIds[storyId]; delete s.storyIds[objectId]; delete s.idToStory[storyId]; delete s.registeredStories[objectId]; delete s.storyBuildHash[storyId]; delete s.storyRequiredHeroData[storyId]; delete s.storyRequiredLevel[storyId]; emit IApplicationEvents.StoryRemoved(objectId, storyId); } function removeStoryPagesMeta(IStoryController.MainState storage s, uint16 storyId, uint maxIterations) external { RemoveStoryContext memory ctx; ctx.storyId = storyId; // --- clean all data related to pages --- EnumerableSet.UintSet storage allPages = s.allStoryPages[ctx.storyId]; ctx.len = allPages.length(); if (ctx.len > maxIterations) { ctx.len = maxIterations; } ctx.tmpPages = new uint[](ctx.len); for (uint i; i < ctx.len; ++i) { ctx.tmpPages[i] = allPages.at(i); ctx.pageId = uint16(ctx.tmpPages[i]); // zero hero class means all classes for (ctx.heroClass = 0; ctx.heroClass < uint(StatLib.HeroClasses.END_SLOT); ++ctx.heroClass) { delete s.answers[ctx.storyId.packStoryPageId(ctx.pageId, ctx.heroClass)]; delete s.nextObjectsRewrite[ctx.storyId.packStoryPageId(ctx.pageId, ctx.heroClass)]; } } // remove all pages for (uint i; i < ctx.tmpPages.length; ++i) { if (!allPages.remove(ctx.tmpPages[i])) { revert IAppErrors.PageNotRemovedError(ctx.tmpPages[i]); } } } function removeStoryAnswersMeta(IStoryController.MainState storage s, uint16 storyId, uint maxIterations) external { RemoveStoryContext memory ctx; ctx.storyId = storyId; // --- clean all data related to answers --- EnumerableSet.Bytes32Set storage allAnswers = s.allStoryAnswers[ctx.storyId]; ctx.len = allAnswers.length(); if (ctx.len > maxIterations) { ctx.len = maxIterations; } ctx.tmpAnswers = new bytes32[](ctx.len); for (uint i; i < ctx.len; ++i) { ctx.answerId = allAnswers.at(i); ctx.tmpAnswers[i] = ctx.answerId; (, ctx.pageId, ctx.heroClass, ctx.answerNum) = ctx.answerId.unpackStoryAnswerId(); delete s.answerAttributes[ctx.answerId]; delete s.attributeRequirements[ctx.answerId]; delete s.itemRequirements[ctx.answerId]; delete s.tokenRequirements[ctx.answerId]; delete s.heroCustomDataRequirement[ctx.answerId]; delete s.globalCustomDataRequirement[ctx.answerId]; delete s.successInfoAttributes[ctx.answerId]; delete s.successInfoStats[ctx.answerId]; delete s.successInfoMintItems[ctx.answerId]; delete s.failInfoAttributes[ctx.answerId]; delete s.failInfoStats[ctx.answerId]; delete s.failInfoMintItems[ctx.answerId]; delete s.burnItem[ctx.answerId]; for (ctx.answerResultId = 0; ctx.answerResultId < uint(IStoryController.AnswerResultId.END_SLOT); ++ctx.answerResultId) { delete s.nextPageIds[ctx.storyId.packStoryNextPagesId( ctx.pageId, ctx.heroClass, ctx.answerNum, ctx.answerResultId )]; } // we assume here, that CustomDataResultId.UNKNOWN = 0 shouldn't be used, so we can skip delete for it for (ctx.customDataResultId = 1; ctx.customDataResultId < uint(IStoryController.CustomDataResult.END_SLOT); ++ctx.customDataResultId) { delete s.customDataResult[ctx.storyId.packStoryCustomDataResult( ctx.pageId, ctx.heroClass, ctx.answerNum, ctx.customDataResultId )]; } } // ATTENTION! need to remove items one by one from sets // remove all answers for (uint i; i < ctx.tmpAnswers.length; ++i) { allAnswers.remove(ctx.tmpAnswers[i]); } } //endregion ------------------ Remove logic //region ------------------ Utils function registerAnswer(IStoryController.MainState storage s, uint16 storyId, bytes32 answerId) internal { s.allStoryAnswers[storyId].add(answerId); } function registerPage(IStoryController.MainState storage s, uint16 storyId, uint16 pageId) internal { s.allStoryPages[storyId].add(pageId); } //endregion ------------------ Utils }
// 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 CannotApplyNotLastBid(); error AuctionGuildWithShelterCannotBid(); //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 }
// 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 }
// 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; }
// 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; } 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; }
// 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); }
// 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); }
// 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); }
// 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; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.23; interface IOracle { function getRandomNumber(uint max, uint seed) external returns (uint); function getRandomNumberInRange(uint min, uint max, uint seed) external returns (uint); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.23; import "../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; }
// 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); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.23; import "../interfaces/IAppErrors.sol"; import "../solady/LibPRNG.sol"; library CalcLib { uint32 public constant MAX_CHANCE = 1e9; function minI32(int32 a, int32 b) internal pure returns (int32) { return a < b ? a : b; } function max32(int32 a, int32 b) internal pure returns (int32) { return a >= b ? a : b; } function absDiff(int32 a, int32 b) internal pure returns (uint32) { if (!((a >= 0 && b >= 0) || (a <= 0 && b <= 0))) revert IAppErrors.AbsDiff(a, b); if (a < 0) { a = - a; } if (b < 0) { b = - b; } return uint32(uint(int(a >= b ? a - b : b - a))); } function toUint(int32 n) internal pure returns (uint) { if (n <= 0) { return 0; } return uint(int(n)); } function toInt32(uint a) internal pure returns (int32){ if (a >= uint(int(type(int32).max))) { return type(int32).max; } return int32(int(a)); } /// @dev Simplified pseudo-random for minor functionality function pseudoRandom(uint maxValue) internal view returns (uint) { if (maxValue == 0) { return 0; } uint salt = genSalt(); // pseudo random number return (uint(keccak256(abi.encodePacked(blockhash(block.number), block.coinbase, block.difficulty, block.number, block.timestamp, tx.gasprice, gasleft(), salt))) % (maxValue + 1)); } function genSalt() internal view returns (uint salt) { // skale has a RNG Endpoint if ( block.chainid == uint(1351057110) || block.chainid == uint(37084624) ) { assembly { let freemem := mload(0x40) let start_addr := add(freemem, 0) if iszero(staticcall(gas(), 0x18, 0, 0, start_addr, 32)) { invalid() } salt := mload(freemem) } } } function pseudoRandomUint32(uint32 maxValue) internal view returns (uint32) { return uint32(pseudoRandom(uint(maxValue))); } /// @notice Generate pseudo-random uint in the range [0..maxValue) using Solady pseudo-random function function nextPrng(LibPRNG.PRNG memory prng, uint maxValue) internal pure returns (uint) { return LibPRNG.next(prng) % maxValue; } /// @notice pseudoRandomUint32 with customizable pseudoRandom() function pseudoRandomUint32Flex( uint32 maxValue, function (uint) internal view returns (uint) random_ ) internal view returns (uint32) { return uint32(random_(uint(maxValue))); } function pseudoRandomInt32(int32 maxValue) internal view returns (int32) { bool neg; if (maxValue < 0) { neg = true; maxValue = - maxValue; } uint32 v = uint32(pseudoRandom(uint(int(maxValue)))); return neg ? - int32(int(uint(v))) : int32(int(uint(v))); } /// @dev Simplified pseudo-random for minor functionality function pseudoRandomWithSeed(uint maxValue, uint seed) internal view returns (uint) { if (maxValue == 0) { return 0; } uint salt = genSalt(); // pseudo random number return (uint(keccak256(abi.encodePacked(blockhash(block.number), block.coinbase, block.difficulty, block.number, block.timestamp, tx.gasprice, gasleft(), seed, salt))) % (maxValue + 1)); } /// @dev Simplified pseudo-random for minor functionality, in range function pseudoRandomInRange(uint min, uint max) internal view returns (uint) { if (min >= max) { return max; } uint r = pseudoRandom(max - min); return min + r; } /// @dev Simplified pseudo-random for minor functionality, in range /// Equal to pseudoRandomInRange(min, max, pseudoRandom) function pseudoRandomInRangeFlex( uint min, uint max, function (uint) internal view returns (uint) random_ ) internal view returns (uint) { return min >= max ? max : min + random_(max - min); } function minusWithZeroFloor(uint a, uint b) internal pure returns (uint){ if (a <= b) { return 0; } return a - b; } function minusWithMinFloorI32(int32 a, int32 b) internal pure returns (int32){ if (int(a) - int(b) < type(int32).min) { return type(int32).min; } return a - b; } function plusWithMaxFloor32(int32 a, int32 b) internal pure returns (int32){ if (int(a) + int(b) >= type(int32).max) { return type(int32).max; } return a + b; } function sqrt(uint x) internal pure returns (uint z) { assembly { // Start off with z at 1. z := 1 // Used below to help find a nearby power of 2. let y := x // Find the lowest power of 2 that is at least sqrt(x). if iszero(lt(y, 0x100000000000000000000000000000000)) { y := shr(128, y) // Like dividing by 2 ** 128. z := shl(64, z) // Like multiplying by 2 ** 64. } if iszero(lt(y, 0x10000000000000000)) { y := shr(64, y) // Like dividing by 2 ** 64. z := shl(32, z) // Like multiplying by 2 ** 32. } if iszero(lt(y, 0x100000000)) { y := shr(32, y) // Like dividing by 2 ** 32. z := shl(16, z) // Like multiplying by 2 ** 16. } if iszero(lt(y, 0x10000)) { y := shr(16, y) // Like dividing by 2 ** 16. z := shl(8, z) // Like multiplying by 2 ** 8. } if iszero(lt(y, 0x100)) { y := shr(8, y) // Like dividing by 2 ** 8. z := shl(4, z) // Like multiplying by 2 ** 4. } if iszero(lt(y, 0x10)) { y := shr(4, y) // Like dividing by 2 ** 4. z := shl(2, z) // Like multiplying by 2 ** 2. } if iszero(lt(y, 0x8)) { // Equivalent to 2 ** z. z := shl(1, z) } // Shifting right by 1 is like dividing by 2. z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) // Compute a rounded down version of z. let zRoundDown := div(x, z) // If zRoundDown is smaller, use it. if lt(zRoundDown, z) { z := zRoundDown } } } /********************************************* * PRB-MATH * * https://github.com/hifi-finance/prb-math * **********************************************/ /// @notice Calculates the binary logarithm of x. /// /// @dev Based on the iterative approximation algorithm. /// https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation /// /// Requirements: /// - x must be greater than or equal to SCALE, otherwise the result would be negative. /// /// Caveats: /// - The results are nor perfectly accurate to the last decimal, /// due to the lossy precision of the iterative approximation. /// /// @param x The unsigned 60.18-decimal fixed-point number for which /// to calculate the binary logarithm. /// @return result The binary logarithm as an unsigned 60.18-decimal fixed-point number. function log2(uint256 x) internal pure returns (uint256 result) { if (x < 1e18) revert IAppErrors.TooLowX(x); // Calculate the integer part of the logarithm // and add it to the result and finally calculate y = x * 2^(-n). uint256 n = mostSignificantBit(x / 1e18); // The integer part of the logarithm as an unsigned 60.18-decimal fixed-point number. // The operation can't overflow because n is maximum 255 and SCALE is 1e18. uint256 rValue = n * 1e18; // This is y = x * 2^(-n). uint256 y = x >> n; // If y = 1, the fractional part is zero. if (y == 1e18) { return rValue; } // Calculate the fractional part via the iterative approximation. // The "delta >>= 1" part is equivalent to "delta /= 2", but shifting bits is faster. for (uint256 delta = 5e17; delta > 0; delta >>= 1) { y = (y * y) / 1e18; // Is y^2 > 2 and so in the range [2,4)? if (y >= 2 * 1e18) { // Add the 2^(-m) factor to the logarithm. rValue += delta; // Corresponds to z/2 on Wikipedia. y >>= 1; } } return rValue; } /// @notice Finds the zero-based index of the first one in the binary representation of x. /// @dev See the note on msb in the "Find First Set" /// Wikipedia article https://en.wikipedia.org/wiki/Find_first_set /// @param x The uint256 number for which to find the index of the most significant bit. /// @return msb The index of the most significant bit as an uint256. //noinspection NoReturn function mostSignificantBit(uint256 x) internal pure returns (uint256 msb) { if (x >= 2 ** 128) { x >>= 128; msb += 128; } if (x >= 2 ** 64) { x >>= 64; msb += 64; } if (x >= 2 ** 32) { x >>= 32; msb += 32; } if (x >= 2 ** 16) { x >>= 16; msb += 16; } if (x >= 2 ** 8) { x >>= 8; msb += 8; } if (x >= 2 ** 4) { x >>= 4; msb += 4; } if (x >= 2 ** 2) { x >>= 2; msb += 2; } if (x >= 2 ** 1) { // No need to shift x any more. msb += 1; } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.23; import "../interfaces/IItemController.sol"; import "../interfaces/IStatController.sol"; import "../interfaces/IAppErrors.sol"; library PackingLib { ////////////////////////// // ---- PACKING LOGIC ---- ////////////////////////// //region ------------------------------------ COMMON function packNftId(address token, uint id) internal pure returns (bytes32 serialized) { if (id > uint(type(uint64).max)) revert IAppErrors.TooHighValue(id); serialized = bytes32(uint(uint160(token))); serialized |= bytes32(uint(uint64(id))) << 160; } function unpackNftId(bytes32 data) internal pure returns (address token, uint id) { token = address(uint160(uint(data))); id = uint(data) >> 160; } function packAddressWithAmount(address token, uint amount) internal pure returns (bytes32 data) { if (amount > uint(type(uint96).max)) revert IAppErrors.TooHighValue(amount); data = bytes32(uint(uint160(token))); data |= bytes32(uint(uint96(amount))) << 160; } function unpackAddressWithAmount(bytes32 data) internal pure returns (address token, uint amount) { token = address(uint160(uint(data))); amount = uint(data) >> 160; } function packItemMintInfo(address item, uint32 chance) internal pure returns (bytes32 data) { data = bytes32(uint(uint160(item))); data |= bytes32(uint(chance)) << 160; } function unpackItemMintInfo(bytes32 data) internal pure returns (address item, uint32 chance) { item = address(uint160(uint(data))); chance = uint32(uint(data) >> 160); } /// @param customDataIndex We assume, that two lowest bytes of this string are always zero /// So, the string looks like following: 0xXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX0000 /// Last 2 bytes will be used to encode {value} function packCustomDataChange(bytes32 customDataIndex, int16 value) internal pure returns (bytes32 data) { if (uint(customDataIndex) != (uint(customDataIndex) >> 16) << 16) revert IAppErrors.IncompatibleInputString(); data = bytes32(uint(customDataIndex)); data |= bytes32(uint(uint16(value))); } function unpackCustomDataChange(bytes32 data) internal pure returns (bytes32 customDataIndex, int16 value) { customDataIndex = bytes32((uint(data) >> 16) << 16); value = int16(int(uint(uint16(uint(data))))); } /// @dev min(uint64) + max(uint64) + isHeroData/isMandatory(uint8) function packCustomDataRequirements(uint64 min, uint64 max, bool key) internal pure returns (bytes32 data) { data = bytes32(uint(min)); data |= bytes32(uint(max)) << 64; data |= bytes32(uint(key ? uint8(1) : uint8(0))) << (64 + 64); } function unpackCustomDataRequirements(bytes32 data) internal pure returns (uint64 min, uint64 max, bool key) { min = uint64(uint(data)); max = uint64(uint(data) >> 64); key = uint8(uint(data) >> (64 + 64)) == uint8(1); } function packStatsChange( uint32 experience, int32 heal, int32 manaRegen, int32 lifeChancesRecovered, int32 damage, int32 manaConsumed ) internal pure returns (bytes32 data) { data = bytes32(uint(experience)); data |= bytes32(uint(uint32(heal))) << 32; data |= bytes32(uint(uint32(manaRegen))) << (32 + 32); data |= bytes32(uint(uint32(lifeChancesRecovered))) << (32 + 32 + 32); data |= bytes32(uint(uint32(damage))) << (32 + 32 + 32 + 32); data |= bytes32(uint(uint32(manaConsumed))) << (32 + 32 + 32 + 32 + 32); } function unpackStatsChange(bytes32 data) internal pure returns ( uint32 experience, int32 heal, int32 manaRegen, int32 lifeChancesRecovered, int32 damage, int32 manaConsumed ) { experience = uint32(uint(data)); heal = int32(int(uint(data) >> 32)); manaRegen = int32(int(uint(data) >> (32 + 32))); lifeChancesRecovered = int32(int(uint(data) >> (32 + 32 + 32))); damage = int32(int(uint(data) >> (32 + 32 + 32 + 32))); manaConsumed = int32(int(uint(data) >> (32 + 32 + 32 + 32 + 32))); } function packNftIdWithValue(address token, uint id, uint32 value) internal pure returns (bytes32 serialized) { if (id > uint(type(uint64).max)) revert IAppErrors.TooHighValue(id); serialized = bytes32(uint(uint160(token))); serialized |= bytes32(uint(uint64(id))) << 160; serialized |= bytes32(uint(value)) << 160 + 64; } function unpackNftIdWithValue(bytes32 data) internal pure returns (address token, uint id, uint32 value) { token = address(uint160(uint(data))); id = uint64(uint(data) >> 160); value = uint32(uint(data) >> 160 + 64); } //endregion ------------------------------------ COMMON //region ------------------------------------ WORLD/BATTLEFIELD MAP function packMapObject(address objectAddress, uint64 objectId, uint8 objectType) internal pure returns (bytes32 packedData) { packedData = bytes32(bytes20(objectAddress)); packedData |= bytes32(uint(objectId) << 32); packedData |= bytes32(uint(objectType) << 24); } function unpackMapObject(bytes32 packedData) internal pure returns (address objectAddress, uint64 objectId, uint8 objectType) { objectAddress = address(bytes20(packedData)); objectId = uint64(uint(packedData) >> 32); objectType = uint8(uint(packedData) >> 24); } function packCoordinate(uint128 x, uint128 y) internal pure returns (bytes32 packedData) { packedData = bytes32(uint(x)); packedData |= bytes32(uint(y) << 128); } function unpackCoordinate(bytes32 packedData) internal pure returns (uint128 x, uint128 y) { x = uint128(uint(packedData)); y = uint128(uint(packedData) >> 128); } /// @param x Assume x <= max uint64 /// @param y Assume y <= max uint64 function packBattlefieldId(uint8 biomeMapFieldId, uint8 territoryNumber, uint128 x, uint128 y) internal pure returns (bytes32 packedData) { // 256 => 128 + 128; // 1) 128 is used for biomeMapFieldId, territoryNumber and probably other fields in the future // 2) 128 is used to store x, y as uint64, uint64 // we will use uint64 for coordinates assuming it is more than enough for biome map packedData = bytes32(uint(biomeMapFieldId)); packedData |= bytes32(uint(territoryNumber) << (8)); packedData |= bytes32(uint(uint64(x)) << 128); packedData |= bytes32(uint(uint64(y)) << (64 + 128)); } function unpackBattlefieldId(bytes32 packedData) internal pure returns (uint8 biomeMapFieldId, uint8 territoryNumber, uint128 x, uint128 y) { biomeMapFieldId = uint8(uint(packedData)); territoryNumber = uint8(uint(packedData) >> (8)); x = uint128(uint64(uint(packedData) >> (128))); y = uint128(uint64(uint(packedData) >> (64 + 128))); } //endregion ------------------------------------ WORLD/BATTLEFIELD MAP //region ------------------------------------ REINFORCEMENT function packReinforcementHeroInfo(uint8 biome, uint128 score, uint8 fee, uint64 stakeTs) internal pure returns (bytes32 packedData) { packedData = bytes32(uint(biome)); packedData |= bytes32(uint(score) << 8); packedData |= bytes32(uint(fee) << (8 + 128)); packedData |= bytes32(uint(stakeTs) << (8 + 128 + 8)); } function unpackReinforcementHeroInfo(bytes32 packedData) internal pure returns (uint8 biome, uint128 score, uint8 fee, uint64 stakeTs) { biome = uint8(uint(packedData)); score = uint128(uint(packedData) >> 8); fee = uint8(uint(packedData) >> (8 + 128)); stakeTs = uint64(uint(packedData) >> (8 + 128 + 8)); } function packConfigReinforcementV2(uint32 min, uint32 max, uint32 lowDivider, uint32 highDivider, uint8 levelLimit) internal pure returns (bytes32 packedData) { packedData = bytes32(uint(min)); packedData |= bytes32(uint(max) << 32); packedData |= bytes32(uint(lowDivider) << 64); packedData |= bytes32(uint(highDivider) << 96); packedData |= bytes32(uint(levelLimit) << 128); } function unpackConfigReinforcementV2(bytes32 packedData) internal pure returns (uint32 min, uint32 max, uint32 lowDivider, uint32 highDivider, uint8 levelLimit) { min = uint32(uint(packedData)); max = uint32(uint(packedData) >> 32); lowDivider = uint32(uint(packedData) >> 64); highDivider = uint32(uint(packedData) >> 96); levelLimit = uint8(uint(packedData) >> 128); } //endregion ------------------------------------ REINFORCEMENT //region ------------------------------------ DUNGEON function packDungeonKey(address heroAdr, uint80 heroId, uint16 dungLogicNum) internal pure returns (bytes32 data) { data = bytes32(uint(uint160(heroAdr))); data |= bytes32(uint(heroId)) << 160; data |= bytes32(uint(dungLogicNum)) << (160 + 80); } function unpackDungeonKey(bytes32 data) internal pure returns (address heroAdr, uint80 heroId, uint16 dungLogicNum) { heroAdr = address(uint160(uint(data))); heroId = uint80(uint(data) >> 160); dungLogicNum = uint16(uint(data) >> (160 + 80)); } // --- GAME OBJECTS --- function packIterationKey(address heroAdr, uint64 heroId, uint32 objId) internal pure returns (bytes32 data) { data = bytes32(uint(uint160(heroAdr))); data |= bytes32(uint(heroId)) << 160; data |= bytes32(uint(objId)) << (160 + 64); } function unpackIterationKey(bytes32 data) internal pure returns (address heroAdr, uint64 heroId, uint32 objId) { heroAdr = address(uint160(uint(data))); heroId = uint64(uint(data) >> 160); objId = uint32(uint(data) >> (160 + 64)); } function packMonsterStats( uint8 level, uint8 race, uint32 experience, uint8 maxDropItems ) internal pure returns (bytes32 data) { data = bytes32(uint(level)); data |= bytes32(uint(race)) << 8; data |= bytes32(uint(experience)) << (8 + 8); data |= bytes32(uint(maxDropItems)) << (8 + 8 + 32); } function unpackMonsterStats(bytes32 data) internal pure returns ( uint8 level, uint8 race, uint32 experience, uint8 maxDropItems ) { level = uint8(uint(data)); race = uint8(uint(data) >> 8); experience = uint32(uint(data) >> (8 + 8)); maxDropItems = uint8(uint(data) >> (8 + 8 + 32)); } function packAttackInfo( address attackToken, uint64 attackTokenId, uint8 attackType ) internal pure returns (bytes32 data) { data = bytes32(uint(uint160(attackToken))); data |= bytes32(uint(attackTokenId)) << 160; data |= bytes32(uint(attackType)) << (160 + 64); } function unpackAttackInfo(bytes32 data) internal pure returns ( address attackToken, uint64 attackTokenId, uint8 attackType ) { attackToken = address(uint160(uint(data))); attackTokenId = uint64(uint(data) >> 160); attackType = uint8(uint(data) >> (160 + 64)); } function packPlayedObjKey(address heroAdr, uint64 heroId, uint8 oType, uint8 biome) internal pure returns (bytes32 data) { data = bytes32(uint(uint160(heroAdr))); data |= bytes32(uint(heroId)) << 160; data |= bytes32(uint(oType)) << (160 + 64); data |= bytes32(uint(biome)) << (160 + 64 + 8); } function unpackPlayedObjKey(bytes32 data) internal pure returns (address heroAdr, uint64 heroId, uint8 oType, uint8 biome) { heroAdr = address(uint160(uint(data))); heroId = uint64(uint(data) >> 160); oType = uint8(uint(data) >> (160 + 64)); biome = uint8(uint(data) >> (160 + 64 + 8)); } function packGeneratedMonster(bool generated, uint32 amplifier, int32 hp, uint8 turnCounter) internal pure returns (bytes32 data) { data = bytes32(uint(uint8(generated ? 1 : 0))); data |= bytes32(uint(amplifier)) << 8; data |= bytes32(uint(uint32(hp))) << (8 + 32); data |= bytes32(uint(turnCounter)) << (8 + 32 + 32); } function unpackGeneratedMonster(bytes32 data) internal pure returns (bool generated, uint32 amplifier, int32 hp, uint8 turnCounter) { generated = uint8(uint(data)) == uint8(1); amplifier = uint32(uint(data) >> 8); hp = int32(int(uint(data) >> (8 + 32))); turnCounter = uint8(uint(data) >> (8 + 32 + 32)); } //endregion ------------------------------------ DUNGEON //region ------------------------------------ ITEMS /// @notice itemMetaType8 + itemLvl8 + itemType8 + baseDurability16 + defaultRarity8 + minAttr8 + maxAttr8 + manaCost32 + req(packed core 128) /// @param itemType This is ItemType enum function packItemMeta( uint8 itemMetaType, uint8 itemLvl, uint8 itemType, uint16 baseDurability, uint8 defaultRarity, uint8 minAttr, uint8 maxAttr, uint32 manaCost, IStatController.CoreAttributes memory req ) internal pure returns (bytes32 data) { data = bytes32(uint(itemMetaType)); data |= bytes32(uint(itemLvl)) << 8; data |= bytes32(uint(itemType)) << (8 + 8); data |= bytes32(uint(baseDurability)) << (8 + 8 + 8); data |= bytes32(uint(defaultRarity)) << (8 + 8 + 8 + 16); data |= bytes32(uint(minAttr)) << (8 + 8 + 8 + 16 + 8); data |= bytes32(uint(maxAttr)) << (8 + 8 + 8 + 16 + 8 + 8); data |= bytes32(uint(manaCost)) << (8 + 8 + 8 + 16 + 8 + 8 + 8); data |= bytes32(uint(int(req.strength))) << (8 + 8 + 8 + 16 + 8 + 8 + 8 + 32); data |= bytes32(uint(int(req.dexterity))) << (8 + 8 + 8 + 16 + 8 + 8 + 8 + 32 + 32); data |= bytes32(uint(int(req.vitality))) << (8 + 8 + 8 + 16 + 8 + 8 + 8 + 32 + 32 + 32); data |= bytes32(uint(int(req.energy))) << (8 + 8 + 8 + 16 + 8 + 8 + 8 + 32 + 32 + 32 + 32); } function unpackItemMeta(bytes32 data) internal pure returns (IItemController.ItemMeta memory) { IItemController.ItemMeta memory result; result.itemMetaType = uint8(uint(data)); result.itemLevel = uint8(uint(data) >> 8); result.itemType = IItemController.ItemType(uint8(uint(data) >> (8 + 8))); result.baseDurability = uint16(uint(data) >> (8 + 8 + 8)); result.defaultRarity = uint8(uint(data) >> (8 + 8 + 8 + 16)); result.minRandomAttributes = uint8(uint(data) >> (8 + 8 + 8 + 16 + 8)); result.maxRandomAttributes = uint8(uint(data) >> (8 + 8 + 8 + 16 + 8 + 8)); result.manaCost = uint32(uint(data) >> (8 + 8 + 8 + 16 + 8 + 8 + 8)); result.requirements.strength = int32(int(uint(data) >> (8 + 8 + 8 + 16 + 8 + 8 + 8 + 32))); result.requirements.dexterity = int32(int(uint(data) >> (8 + 8 + 8 + 16 + 8 + 8 + 8 + 32 + 32))); result.requirements.vitality = int32(int(uint(data) >> (8 + 8 + 8 + 16 + 8 + 8 + 8 + 32 + 32 + 32))); result.requirements.energy = int32(int(uint(data) >> (8 + 8 + 8 + 16 + 8 + 8 + 8 + 32 + 32 + 32 + 32))); return result; } function packItemGenerateInfo(uint8 id, int32 min, int32 max, uint32 chance) internal pure returns (bytes32 data) { data = bytes32(uint(id)); data |= bytes32(uint(uint32(min))) << 8; data |= bytes32(uint(uint32(max))) << (8 + 32); data |= bytes32(uint(chance)) << (8 + 32 + 32); } function unpackItemGenerateInfo(bytes32 data) internal pure returns (uint8 id, int32 min, int32 max, uint32 chance) { id = uint8(uint(data)); min = int32(int(uint(data) >> 8)); max = int32(int(uint(data) >> (8 + 32))); chance = uint32(uint(data) >> (8 + 32 + 32)); } function packItemAttackInfo( uint8 attackType, int32 min, int32 max, int32 factorStr, int32 factorDex, int32 factorVit, int32 factorEng ) internal pure returns (bytes32 data) { data = bytes32(uint(attackType)); data |= bytes32(uint(uint32(min))) << 8; data |= bytes32(uint(uint32(max))) << (8 + 32); data |= bytes32(uint(int(factorStr))) << (8 + 32 + 32); data |= bytes32(uint(int(factorDex))) << (8 + 32 + 32 + 32); data |= bytes32(uint(int(factorVit))) << (8 + 32 + 32 + 32 + 32); data |= bytes32(uint(int(factorEng))) << (8 + 32 + 32 + 32 + 32 + 32); } function unpackItemAttackInfo(bytes32 data) internal pure returns ( uint8 attackType, int32 min, int32 max, int32 factorStr, int32 factorDex, int32 factorVit, int32 factorEng ) { attackType = uint8(uint(data)); min = int32(int(uint(data) >> 8)); max = int32(int(uint(data) >> (8 + 32))); factorStr = int32(int(uint(data) >> (8 + 32 + 32))); factorDex = int32(int(uint(data) >> (8 + 32 + 32 + 32))); factorVit = int32(int(uint(data) >> (8 + 32 + 32 + 32 + 32))); factorEng = int32(int(uint(data) >> (8 + 32 + 32 + 32 + 32 + 32))); } function packItemInfo(uint8 rarity, uint8 augmentationLevel, uint16 durability) internal pure returns (bytes32 data) { data = bytes32(uint(rarity)); data |= bytes32(uint(augmentationLevel)) << 8; data |= bytes32(uint(durability)) << (8 + 8); } function unpackItemInfo(bytes32 data) internal pure returns (uint8 rarity, uint8 augmentationLevel, uint16 durability) { rarity = uint8(uint(data)); augmentationLevel = uint8(uint(data) >> 8); durability = uint16(uint(data) >> (8 + 8)); } //endregion ------------------------------------ ITEMS //region ------------------------------------ STORIES function packStoryPageId(uint16 storyId, uint16 pageId, uint8 heroClass) internal pure returns (bytes32 data) { data = bytes32(uint(storyId)); data |= bytes32(uint(pageId)) << 16; data |= bytes32(uint(heroClass)) << (16 + 16); } function unpackStoryPageId(bytes32 data) internal pure returns (uint16 storyId, uint16 pageId, uint8 heroClass) { storyId = uint16(uint(data)); pageId = uint16(uint(data) >> 16); heroClass = uint8(uint(data) >> (16 + 16)); } function packStoryAnswerId(uint16 storyId, uint16 pageId, uint8 heroClass, uint16 answerId) internal pure returns (bytes32 data) { data = bytes32(uint(storyId)); data |= bytes32(uint(pageId)) << 16; data |= bytes32(uint(heroClass)) << (16 + 16); data |= bytes32(uint(answerId)) << (16 + 16 + 8); } function unpackStoryAnswerId(bytes32 data) internal pure returns (uint16 storyId, uint16 pageId, uint8 heroClass, uint16 answerId) { storyId = uint16(uint(data)); pageId = uint16(uint(data) >> 16); heroClass = uint8(uint(data) >> (16 + 16)); answerId = uint16(uint(data) >> (16 + 16 + 8)); } function packStoryNextPagesId(uint16 storyId, uint16 pageId, uint8 heroClass, uint16 answerId, uint8 resultId) internal pure returns (bytes32 data) { data = bytes32(uint(storyId)); data |= bytes32(uint(pageId)) << 16; data |= bytes32(uint(heroClass)) << (16 + 16); data |= bytes32(uint(answerId)) << (16 + 16 + 8); data |= bytes32(uint(resultId)) << (16 + 16 + 8 + 16); } function unpackStoryNextPagesId(bytes32 data) internal pure returns (uint16 storyId, uint16 pageId, uint8 heroClass, uint16 answerId, uint8 resultId) { storyId = uint16(uint(data)); pageId = uint16(uint(data) >> 16); heroClass = uint8(uint(data) >> (16 + 16)); answerId = uint16(uint(data) >> (16 + 16 + 8)); resultId = uint8(uint(data) >> (16 + 16 + 8 + 16)); } function packStoryAttributeRequirement(uint8 attributeIndex, int32 value, bool isCore) internal pure returns (bytes32 data) { data = bytes32(uint(attributeIndex)); data |= bytes32(uint(uint32(value))) << 8; data |= bytes32(uint(isCore ? uint8(1) : uint8(0))) << (8 + 32); } function unpackStoryAttributeRequirement(bytes32 data) internal pure returns (uint8 attributeIndex, int32 value, bool isCore) { attributeIndex = uint8(uint(data)); value = int32(int(uint(data) >> 8)); isCore = uint8(uint(data) >> (8 + 32)) == uint8(1); } function packStoryItemRequirement(address item, bool requireItemBurn, bool requireItemEquipped) internal pure returns (bytes32 data) { data = bytes32(uint(uint160(item))); data |= bytes32(uint(requireItemBurn ? uint8(1) : uint8(0))) << 160; data |= bytes32(uint(requireItemEquipped ? uint8(1) : uint8(0))) << (160 + 8); } function unpackStoryItemRequirement(bytes32 data) internal pure returns (address item, bool requireItemBurn, bool requireItemEquipped) { item = address(uint160(uint(data))); requireItemBurn = uint8(uint(data) >> 160) == uint8(1); requireItemEquipped = uint8(uint(data) >> (160 + 8)) == uint8(1); } /// @dev max amount is 309,485,009 for token with 18 decimals function packStoryTokenRequirement(address token, uint88 amount, bool requireTransfer) internal pure returns (bytes32 data) { data = bytes32(uint(uint160(token))); data |= bytes32(uint(amount)) << 160; data |= bytes32(uint(requireTransfer ? uint8(1) : uint8(0))) << (160 + 88); } function unpackStoryTokenRequirement(bytes32 data) internal pure returns (address token, uint88 amount, bool requireTransfer) { token = address(uint160(uint(data))); amount = uint88(uint(data) >> 160); requireTransfer = uint8(uint(data) >> (160 + 88)) == uint8(1); } function packStoryCustomDataResult(uint16 storyId, uint16 pageId, uint8 heroClass, uint16 answerId, uint8 customDataResultId) internal pure returns (bytes32 data) { data = bytes32(uint(storyId)); data |= bytes32(uint(pageId)) << 16; data |= bytes32(uint(heroClass)) << (16 + 16); data |= bytes32(uint(answerId)) << (16 + 16 + 8); data |= bytes32(uint(customDataResultId)) << (16 + 16 + 8 + 16); } function unpackStoryCustomDataResult(bytes32 data) internal pure returns (uint16 storyId, uint16 pageId, uint8 heroClass, uint16 answerId, uint8 customDataResultId) { storyId = uint16(uint(data)); pageId = uint16(uint(data) >> 16); heroClass = uint8(uint(data) >> (16 + 16)); answerId = uint16(uint(data) >> (16 + 16 + 8)); customDataResultId = uint8(uint(data) >> (16 + 16 + 8 + 16)); } function packStoryHeroState(uint16 pageId, uint40 heroLastActionTS) internal pure returns (bytes32 data) { data = bytes32(uint(pageId)); data |= bytes32(uint(heroLastActionTS)) << 16; } function unpackStoryHeroState(bytes32 data) internal pure returns (uint16 pageId, uint40 heroLastActionTS) { pageId = uint16(uint(data)); heroLastActionTS = uint40(uint(data) >> 16); } function packStoryHeroStateId(address heroAdr, uint80 heroId, uint16 storyId) internal pure returns (bytes32 data) { data = bytes32(uint(uint160(heroAdr))); data |= bytes32(uint(heroId)) << 160; data |= bytes32(uint(storyId)) << (160 + 80); } function unpackStoryHeroStateId(bytes32 data) internal pure returns (address heroAdr, uint80 heroId, uint16 storyId) { heroAdr = address(uint160(uint(data))); heroId = uint80(uint(data) >> 160); storyId = uint16(uint(data) >> (160 + 80)); } function packStorySimpleRequirement(uint32 randomRequirement, uint32 delayRequirement, bool isFinalAnswer) internal pure returns (bytes32 data) { data = bytes32(uint(randomRequirement)); data |= bytes32(uint(delayRequirement)) << 32; data |= bytes32(uint(isFinalAnswer ? uint8(1) : uint8(0))) << (32 + 32); } function unpackStorySimpleRequirement(bytes32 data) internal pure returns (uint32 randomRequirement, uint32 delayRequirement, bool isFinalAnswer) { randomRequirement = uint32(uint(data)); delayRequirement = uint32(uint(data) >> 32); isFinalAnswer = uint8(uint(data) >> (32 + 32)) == uint8(1); } function packBreakInfo(uint8 slot, uint64 chance, bool stopIfBroken) internal pure returns (bytes32 data) { data = bytes32(uint(slot)); data |= bytes32(uint(chance)) << 8; data |= bytes32(uint(stopIfBroken ? uint8(1) : uint8(0))) << (8 + 64); } function unpackBreakInfo(bytes32 data) internal pure returns (uint8 slot, uint64 chance, bool stopIfBurned) { slot = uint8(uint(data)); chance = uint64(uint(data) >> 8); stopIfBurned = uint8(uint(data) >> (8 + 64)) == uint8(1); } //endregion ------------------------------------ STORIES //region ------------------------------------ Hero controller function packTierHero(uint8 tier, address hero) internal pure returns (bytes32 packedTierHero) { packedTierHero = bytes32(uint(tier)); packedTierHero |= bytes32(uint(uint160(hero)) << 8); } function unpackTierHero(bytes32 packedTierHero) internal pure returns (uint8 tier, address hero) { tier = uint8(uint(packedTierHero)); hero = address(uint160(uint(packedTierHero) >> 8)); } //endregion ------------------------------------ Hero controller //////////////////////////////////////////////////////////////////////////////////// // ---- ARRAYS LOGIC ---- //////////////////////////////////////////////////////////////////////////////////// //region ------------------------------------ SIMPLE ARRAYS function packUint8Array(uint8[] memory data) internal pure returns (bytes32) { uint len = data.length; if (len > 32) revert IAppErrors.OutOfBounds(len, 32); bytes32 result; for (uint i = 0; i < len; i++) { result |= bytes32(uint(data[i])) << (i * 8); } return result; } /// @notice Simple faster version of {packUint8Array} for small number of items /// It allows to exclude dynamic array creation. function packUint8Array3(uint8 a, uint8 b, uint8 c) internal pure returns (bytes32) { bytes32 result = bytes32(uint(a)); result |= bytes32(uint(b)) << (1 * 8); result |= bytes32(uint(c)) << (2 * 8); return result; } function unpackUint8Array(bytes32 data) internal pure returns (uint8[] memory) { uint8[] memory result = new uint8[](32); for (uint i = 0; i < 32; i++) { result[i] = uint8(uint(data) >> (i * 8)); } return result; } /// @notice Simple faster version of {unpackUint8Array} for small number of items /// It allows to exclude only first 3 values function unpackUint8Array3(bytes32 data) internal pure returns (uint8 a, uint8 b, uint8 c) { a = uint8(uint(data)); b = uint8(uint(data) >> (1 * 8)); c = uint8(uint(data) >> (2 * 8)); } function changeUnit8ArrayWithCheck(bytes32 data, uint index, uint8 value, uint8 expectedPrevValue) internal pure returns (bytes32 newData) { uint8[] memory arr = unpackUint8Array(data); if (arr[index] != expectedPrevValue) revert IAppErrors.UnexpectedValue(uint(expectedPrevValue), uint(arr[index])); arr[index] = value; return packUint8Array(arr); } function packInt32Array(int32[] memory data) internal pure returns (bytes32) { uint len = data.length; if (len > 8) revert IAppErrors.OutOfBounds(len, 8); bytes32 result; for (uint i; i < len; i++) { result |= bytes32(uint(uint32(data[i]))) << (i * 32); } return result; } function unpackInt32Array(bytes32 data) internal pure returns (int32[] memory) { int32[] memory result = new int32[](8); for (uint i = 0; i < 8; i++) { result[i] = int32(int(uint(data) >> (i * 32))); } return result; } function packUint32Array(uint32[] memory data) internal pure returns (bytes32) { uint len = data.length; if (len > 8) revert IAppErrors.OutOfBounds(len, 8); bytes32 result; for (uint i = 0; i < len; i++) { result |= bytes32(uint(data[i])) << (i * 32); } return result; } function unpackUint32Array(bytes32 data) internal pure returns (uint32[] memory) { uint32[] memory result = new uint32[](8); for (uint i = 0; i < 8; i++) { result[i] = uint32(uint(data) >> (i * 32)); } return result; } //endregion ------------------------------------ SIMPLE ARRAYS //region ------------------------------------ COMPLEX ARRAYS // We should represent arrays without concrete size. // For this reason we must not revert IAppErrors.on out of bounds but return zero value instead. // we need it for properly unpack packed arrays with ids // function getInt32AsInt24(bytes32[] memory arr, uint idx) internal pure returns (int32) { // if (idx / 8 >= arr.length) { // return int32(0); // } // return int32(int24(int(uint(arr[idx / 8]) >> ((idx % 8) * 32)))); // } // we need it for properly unpack packed arrays with ids // function getUnit8From32Step(bytes32[] memory arr, uint idx) internal pure returns (uint8) { // if (idx / 8 >= arr.length) { // return uint8(0); // } // return uint8(uint(arr[idx / 8]) >> ((idx % 8) * 32 + 24)); // } function getInt32Memory(bytes32[] memory arr, uint idx) internal pure returns (int32) { if (idx / 8 >= arr.length) { return int32(0); } return int32(int(uint(arr[idx / 8]) >> ((idx % 8) * 32))); } function getInt32(bytes32[] storage arr, uint idx) internal view returns (int32) { // additional gas usage, but we should not revert IAppErrors.on out of bounds if (idx / 8 >= arr.length) { return int32(0); } return int32(int(uint(arr[idx / 8]) >> ((idx % 8) * 32))); } function setInt32(bytes32[] storage arr, uint idx, int32 value) internal { uint pos = idx / 8; uint shift = (idx % 8) * 32; uint curLength = arr.length; if (pos >= curLength) { arr.push(0); for (uint i = curLength; i < pos; ++i) { arr.push(0); } } arr[pos] = bytes32(uint(arr[pos]) & ~(uint(0xffffffff) << shift) | (uint(uint32(value)) & 0xffffffff) << shift); } /// @notice Increment {idx}-th item on {value} function changeInt32(bytes32[] storage arr, uint idx, int32 value) internal returns (int32 newValue, int32 change) { int32 cur = int32(int(getInt32(arr, idx))); int newValueI = int(cur) + int(value); newValue = int32(newValueI); change = int32(newValueI - int(cur)); setInt32(arr, idx, newValue); } function toInt32Array(bytes32[] memory arr, uint size) internal pure returns (int32[] memory) { int32[] memory result = new int32[](size); for (uint i = 0; i < arr.length; i++) { for (uint j; j < 8; ++j) { uint idx = i * 8 + j; if (idx >= size) break; result[idx] = getInt32Memory(arr, idx); } } return result; } /// @dev pack int32 array into bytes32 array function toBytes32Array(int32[] memory arr) internal pure returns (bytes32[] memory) { uint size = arr.length / 8 + 1; bytes32[] memory result = new bytes32[](size); for (uint i; i < size; ++i) { for (uint j; j < 8; ++j) { uint idx = i * 8 + j; if (idx >= arr.length) break; result[i] |= bytes32(uint(uint32(arr[idx]))) << (j * 32); } } return result; } /// @dev pack int32 array into bytes32 array using last 8bytes for ids /// we can not use zero values coz will not able to properly unpack it later function toBytes32ArrayWithIds(int32[] memory arr, uint8[] memory ids) internal pure returns (bytes32[] memory) { if (arr.length != ids.length) revert IAppErrors.LengthsMismatch(); uint size = arr.length / 8 + 1; bytes32[] memory result = new bytes32[](size); for (uint i; i < size; ++i) { for (uint j; j < 8; ++j) { uint idx = i * 8 + j; if (idx >= arr.length) break; if (arr[idx] > type(int24).max || arr[idx] < type(int24).min) revert IAppErrors.IntOutOfRange(int(arr[idx])); if (arr[idx] == 0) revert IAppErrors.ZeroValue(); result[i] |= bytes32(uint(uint24(int24(arr[idx])))) << (j * 32); result[i] |= bytes32(uint(ids[idx])) << (j * 32 + 24); } } return result; } /// @dev we do not know exact size of array, assume zero values is not acceptable for this array function toInt32ArrayWithIds(bytes32[] memory arr) internal pure returns (int32[] memory values, uint8[] memory ids) { uint len = arr.length; uint size = len * 8; int32[] memory valuesTmp = new int32[](size); uint8[] memory idsTmp = new uint8[](size); uint counter; for (uint i = 0; i < len; i++) { for (uint j; j < 8; ++j) { uint idx = i * 8 + j; // if (idx >= size) break; // it looks like a useless check valuesTmp[idx] = int32(int24(int(uint(arr[i]) >> (j * 32)))); // getInt32AsInt24(arr, idx); idsTmp[idx] = uint8(uint(arr[i]) >> (j * 32 + 24)); // getUnit8From32Step(arr, idx); if (valuesTmp[idx] == 0) { break; } counter++; } } values = new int32[](counter); ids = new uint8[](counter); for (uint i; i < counter; ++i) { values[i] = valuesTmp[i]; ids[i] = idsTmp[i]; } } //endregion ------------------------------------ COMPLEX ARRAYS //region ------------------------------------ Guilds /// @dev ShelterID is uint. But in the code we assume that this ID can be stored as uint64 (see auctions) /// @param biome 1, 2, 3... /// @param shelterLevel 1, 2 or 3. /// @param shelterIndex 0, 1, 2 ... function packShelterId(uint8 biome, uint8 shelterLevel, uint8 shelterIndex) internal pure returns (uint) { return uint(biome) | (uint(shelterLevel) << 8) | (uint(shelterIndex) << 16); } function unpackShelterId(uint shelterId) internal pure returns (uint8 biome, uint8 shelterLevel, uint8 shelterIndex) { return (uint8(shelterId), uint8(shelterId >> 8), uint8(shelterId >> 16)); } //endregion ------------------------------------ Guilds //region ------------------------------------ Metadata of IItemController.OtherSubtypeKind function getOtherItemTypeKind(bytes memory packedData) internal pure returns (IItemController.OtherSubtypeKind) { bytes32 serialized; assembly { serialized := mload(add(packedData, 32)) } uint8 kind = uint8(uint(serialized)); if (kind == 0 || kind >= uint8(IItemController.OtherSubtypeKind.END_SLOT)) revert IAppErrors.IncorrectOtherItemTypeKind(kind); return IItemController.OtherSubtypeKind(kind); } function packOtherItemReduceFragility(uint value) internal pure returns (bytes memory packedData) { bytes32 serialized = bytes32(uint(uint8(IItemController.OtherSubtypeKind.REDUCE_FRAGILITY_1))); serialized |= bytes32(uint(uint248(value))) << 8; return bytes.concat(serialized); } function unpackOtherItemReduceFragility(bytes memory packedData) internal pure returns (uint) { bytes32 serialized; assembly { serialized := mload(add(packedData, 32)) } uint8 kind = uint8(uint(serialized)); if (kind != uint8(IItemController.OtherSubtypeKind.REDUCE_FRAGILITY_1)) revert IAppErrors.IncorrectOtherItemTypeKind(kind); uint value = uint248(uint(serialized) >> 8); return value; } //endregion ------------------------------------ Metadata of IItemController.OtherSubtypeKind }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.23; import "../interfaces/IStatController.sol"; import "../interfaces/IHeroController.sol"; import "../interfaces/IAppErrors.sol"; import "../openzeppelin/Math.sol"; import "./CalcLib.sol"; import "./PackingLib.sol"; library StatLib { using PackingLib for bytes32[]; using PackingLib for bytes32; using PackingLib for uint32[]; using PackingLib for int32[]; using CalcLib for int32; //region --------------------------- Constants /// @notice Version of the contract /// @dev Should be incremented when contract changed string public constant STAT_LIB_VERSION = "1.0.0"; uint32 public constant MAX_LEVEL = 99; uint public constant BASE_EXPERIENCE = 100_000; uint public constant BIOME_LEVEL_STEP = 5; uint internal constant _MAX_AMPLIFIER = 1e18; uint private constant _PRECISION = 1e18; uint private constant VIRTUAL_LEVEL_GAP = 2; /// @dev Assume MAX_BIOME * BIOME_LEVEL_STEP < MAX_LEVEL + 1, see dungeonTreasuryReward uint public constant MAX_POSSIBLE_BIOME = 19; //endregion --------------------------- Constants //region --------------------------- Data types struct BaseMultiplier { uint minDamage; uint maxDamage; uint attackRating; uint defense; uint blockRating; uint life; uint mana; } struct LevelUp { uint life; uint mana; } struct InitialHero { IStatController.CoreAttributes core; BaseMultiplier multiplier; LevelUp levelUp; int32 baseLifeChances; } enum HeroClasses { UNKNOWN, THRALL, SAVAGE, MAGE, ASSASSIN, GHOST, HAMMERGINA, END_SLOT } //endregion --------------------------- Data types //region --------------------------- BASE function isNetworkWithOldSavage() public view returns (bool) { return block.chainid == uint(111188) || block.chainid == uint(250); } // --- HERO 1 (Slave) --- function initialHero1() internal pure returns (InitialHero memory) { return InitialHero({ core: IStatController.CoreAttributes({ strength: 15, dexterity: 15, vitality: 30, energy: 10 }), multiplier: BaseMultiplier({ minDamage: 0.1e18, maxDamage: 0.2e18, attackRating: 2e18, defense: 2e18, blockRating: 0.1e18, life: 1.5e18, mana: 0.5e18 }), levelUp: LevelUp({ life: 2e18, mana: 1e18 }), baseLifeChances: 5 }); } // --- HERO 2 (Spata) --- function initialHero2() internal view returns (InitialHero memory) { bool old = isNetworkWithOldSavage(); return InitialHero({ core: IStatController.CoreAttributes({ strength: 30, dexterity: 5, vitality: 25, energy: 10 }), multiplier: BaseMultiplier({ minDamage: 0.15e18, maxDamage: old ? 0.25e18 : 0.5e18, attackRating: old ? 2e18 : 3e18, defense: 1e18, blockRating: 0.08e18, life: 1.3e18, mana: 0.5e18 }), levelUp: LevelUp({ life: 1.8e18, mana: 1e18 }), baseLifeChances: 5 }); } // --- HERO 3 (Decidia) --- function initialHero3() internal pure returns (InitialHero memory) { return InitialHero({ core: IStatController.CoreAttributes({ strength: 10, dexterity: 15, vitality: 20, energy: 25 }), multiplier: BaseMultiplier({ minDamage: 0.1e18, maxDamage: 0.2e18, attackRating: 2e18, defense: 1e18, blockRating: 0.1e18, life: 1e18, mana: 2e18 }), levelUp: LevelUp({ life: 1.3e18, mana: 2e18 }), baseLifeChances: 5 }); } // --- HERO 4 (Innatus) --- function initialHero4() internal pure returns (InitialHero memory) { return InitialHero({ core: IStatController.CoreAttributes({ strength: 15, dexterity: 25, vitality: 15, energy: 15 }), multiplier: BaseMultiplier({ minDamage: 0.1e18, maxDamage: 0.2e18, attackRating: 4e18, defense: 3e18, blockRating: 0.2e18, life: 1.2e18, mana: 1e18 }), levelUp: LevelUp({ life: 1.7e18, mana: 1.5e18 }), baseLifeChances: 5 }); } // --- HERO 5 (F2P) --- function initialHero5() internal pure returns (InitialHero memory) { return InitialHero({ core: IStatController.CoreAttributes({ strength: 20, dexterity: 20, vitality: 20, energy: 10 }), multiplier: BaseMultiplier({ minDamage: 0.15e18, maxDamage: 0.25e18, attackRating: 3e18, defense: 2.5e18, blockRating: 0.15e18, life: 1.5e18, mana: 1.5e18 }), levelUp: LevelUp({ life: 1.5e18, mana: 1.5e18 }), baseLifeChances: 1 }); } // --- HERO 6 (F2P) HAMMERGINA --- function initialHero6() internal pure returns (InitialHero memory) { return InitialHero({ core: IStatController.CoreAttributes({ strength: 50, dexterity: 30, vitality: 50, energy: 15 }), multiplier: BaseMultiplier({ minDamage: 0.2e18, maxDamage: 0.3e18, attackRating: 5e18, defense: 3e18, blockRating: 0.15e18, life: 2e18, mana: 2e18 }), levelUp: LevelUp({ life: 1.7e18, mana: 1.5e18 }), baseLifeChances: 1 }); } // ------ function initialHero(uint heroClass) internal view returns (InitialHero memory) { if (heroClass == 1) { return initialHero1(); } else if (heroClass == 2) { return initialHero2(); } else if (heroClass == 3) { return initialHero3(); } else if (heroClass == 4) { return initialHero4(); } else if (heroClass == 5) { return initialHero5(); } else if (heroClass == 6) { return initialHero6(); } else { revert IAppErrors.UnknownHeroClass(heroClass); } } //endregion --------------------------- BASE //region --------------------------- CALCULATIONS function minDamage(int32 strength, uint heroClass) internal view returns (int32) { return int32(int(strength.toUint() * initialHero(heroClass).multiplier.minDamage / _PRECISION)); } function maxDamage(int32 strength, uint heroClass) internal view returns (int32){ return int32(int(strength.toUint() * initialHero(heroClass).multiplier.maxDamage / _PRECISION)); } function attackRating(int32 dexterity, uint heroClass) internal view returns (int32){ return int32(int(dexterity.toUint() * initialHero(heroClass).multiplier.attackRating / _PRECISION)); } function defense(int32 dexterity, uint heroClass) internal view returns (int32){ return int32(int(dexterity.toUint() * initialHero(heroClass).multiplier.defense / _PRECISION)); } function blockRating(int32 dexterity, uint heroClass) internal view returns (int32){ return int32(int(Math.min((dexterity.toUint() * initialHero(heroClass).multiplier.blockRating / _PRECISION), 75))); } function life(int32 vitality, uint heroClass, uint32 level) internal view returns (int32){ return int32(int( (vitality.toUint() * initialHero(heroClass).multiplier.life / _PRECISION) + (uint(level) * initialHero(heroClass).levelUp.life / _PRECISION) )); } function mana(int32 energy, uint heroClass, uint32 level) internal view returns (int32){ return int32(int( (energy.toUint() * initialHero(heroClass).multiplier.mana / _PRECISION) + (uint(level) * initialHero(heroClass).levelUp.mana / _PRECISION) )); } function lifeChances(uint heroClass, uint32 /*level*/) internal view returns (int32){ return initialHero(heroClass).baseLifeChances; } function levelExperience(uint32 level) internal pure returns (uint32) { if (level == 0 || level >= MAX_LEVEL) { return 0; } return uint32(uint(level) * BASE_EXPERIENCE * (67e17 - CalcLib.log2((uint(MAX_LEVEL - level + 2)) * 1e18)) / 1e18); } function chanceToHit( uint attackersAttackRating, uint defendersDefenceRating, uint attackersLevel, uint defendersLevel, uint arFactor ) internal pure returns (uint) { attackersAttackRating += attackersAttackRating * arFactor / 100; uint x = Math.max(attackersAttackRating, 1); uint y = Math.max(attackersAttackRating + defendersDefenceRating, 1); uint z = attackersLevel; uint k = defendersLevel / 2; uint xy = x * 1e18 / y; uint zk = z * 1e18 / (attackersLevel + k); uint base = 2 * xy * zk / 1e18; return Math.max(Math.min(base, 0.95e18), 0.2e18); } function experienceToVirtualLevel(uint experience, uint startFromLevel) internal pure returns (uint level) { level = startFromLevel; for (; level < MAX_LEVEL;) { if (levelExperience(uint32(level)) >= (experience + 1)) { break; } unchecked{++level;} } } function expPerMonster(uint32 monsterExp, uint monsterRarity, uint32 /*heroExp*/, uint32 /*heroCurrentLvl*/, uint /*monsterBiome*/) internal pure returns (uint32) { // do not reduce exp per level, it is no economical sense return uint32(uint(monsterExp) + uint(monsterExp) * monsterRarity / _MAX_AMPLIFIER); } /// @notice Allow to calculate delta param for {mintDropChance} function mintDropChanceDelta(uint heroCurrentExp, uint heroCurrentLevel, uint monsterBiome) internal pure returns (uint) { uint heroBiome = getVirtualLevel(heroCurrentExp, heroCurrentLevel, true) / StatLib.BIOME_LEVEL_STEP + 1; return heroBiome > monsterBiome ? 2 ** (heroBiome - monsterBiome + 10) : 0; } function getVirtualLevel(uint heroCurrentExp, uint heroCurrentLevel, bool withGap) internal pure returns (uint) { uint virtualLevel = StatLib.experienceToVirtualLevel(heroCurrentExp, heroCurrentLevel); if (withGap && (virtualLevel + 1) > VIRTUAL_LEVEL_GAP) { virtualLevel -= VIRTUAL_LEVEL_GAP; } return virtualLevel; } function initAttributes( bytes32[] storage attributes, uint heroClass, uint32 level, IStatController.CoreAttributes memory base ) internal returns (uint32[] memory result) { attributes.setInt32(uint(IStatController.ATTRIBUTES.STRENGTH), base.strength); attributes.setInt32(uint(IStatController.ATTRIBUTES.DEXTERITY), base.dexterity); attributes.setInt32(uint(IStatController.ATTRIBUTES.VITALITY), base.vitality); attributes.setInt32(uint(IStatController.ATTRIBUTES.ENERGY), base.energy); attributes.setInt32(uint(IStatController.ATTRIBUTES.DAMAGE_MIN), minDamage(base.strength, heroClass)); attributes.setInt32(uint(IStatController.ATTRIBUTES.DAMAGE_MAX), maxDamage(base.strength, heroClass)); attributes.setInt32(uint(IStatController.ATTRIBUTES.ATTACK_RATING), attackRating(base.dexterity, heroClass)); attributes.setInt32(uint(IStatController.ATTRIBUTES.DEFENSE), defense(base.dexterity, heroClass)); attributes.setInt32(uint(IStatController.ATTRIBUTES.BLOCK_RATING), blockRating(base.dexterity, heroClass)); attributes.setInt32(uint(IStatController.ATTRIBUTES.LIFE), life(base.vitality, heroClass, level)); attributes.setInt32(uint(IStatController.ATTRIBUTES.MANA), mana(base.energy, heroClass, level)); attributes.setInt32(uint(IStatController.ATTRIBUTES.LIFE_CHANCES), lifeChances(heroClass, level)); result = new uint32[](3); result[0] = uint32(life(base.vitality, heroClass, level).toUint()); result[1] = uint32(mana(base.energy, heroClass, level).toUint()); result[2] = uint32(lifeChances(heroClass, uint32(level)).toUint()); } function updateCoreDependAttributesInMemory( int32[] memory attributes, int32[] memory bonus, uint heroClass, uint32 level ) internal view returns (int32[] memory) { int32 strength = attributes[uint(IStatController.ATTRIBUTES.STRENGTH)]; int32 dexterity = attributes[uint(IStatController.ATTRIBUTES.DEXTERITY)]; int32 vitality = attributes[uint(IStatController.ATTRIBUTES.VITALITY)]; int32 energy = attributes[uint(IStatController.ATTRIBUTES.ENERGY)]; attributes[uint(IStatController.ATTRIBUTES.DAMAGE_MIN)] = minDamage(strength, heroClass) + bonus[uint(IStatController.ATTRIBUTES.DAMAGE_MIN)]; attributes[uint(IStatController.ATTRIBUTES.DAMAGE_MAX)] = maxDamage(strength, heroClass) + bonus[uint(IStatController.ATTRIBUTES.DAMAGE_MAX)]; attributes[uint(IStatController.ATTRIBUTES.ATTACK_RATING)] = attackRating(dexterity, heroClass) + bonus[uint(IStatController.ATTRIBUTES.ATTACK_RATING)]; attributes[uint(IStatController.ATTRIBUTES.DEFENSE)] = defense(dexterity, heroClass) + bonus[uint(IStatController.ATTRIBUTES.DEFENSE)]; attributes[uint(IStatController.ATTRIBUTES.BLOCK_RATING)] = blockRating(dexterity, heroClass) + bonus[uint(IStatController.ATTRIBUTES.BLOCK_RATING)]; attributes[uint(IStatController.ATTRIBUTES.LIFE)] = life(vitality, heroClass, level) + bonus[uint(IStatController.ATTRIBUTES.LIFE)]; attributes[uint(IStatController.ATTRIBUTES.MANA)] = mana(energy, heroClass, level) + bonus[uint(IStatController.ATTRIBUTES.MANA)]; return attributes; } function updateCoreDependAttributes( IController controller, bytes32[] storage attributes, bytes32[] storage bonusMain, bytes32[] storage bonusExtra, IStatController.ChangeableStats memory _heroStats, uint index, address heroToken, int32 base ) internal { uint heroClass = IHeroController(controller.heroController()).heroClass(heroToken); if (index == uint(IStatController.ATTRIBUTES.STRENGTH)) { attributes.setInt32(uint(IStatController.ATTRIBUTES.DAMAGE_MIN), StatLib.minDamage(base, heroClass) + bonusMain.getInt32(uint(IStatController.ATTRIBUTES.DAMAGE_MIN)) + bonusExtra.getInt32(uint(IStatController.ATTRIBUTES.DAMAGE_MIN)) ); attributes.setInt32(uint(IStatController.ATTRIBUTES.DAMAGE_MAX), StatLib.maxDamage(base, heroClass) + bonusMain.getInt32(uint(IStatController.ATTRIBUTES.DAMAGE_MAX)) + bonusExtra.getInt32(uint(IStatController.ATTRIBUTES.DAMAGE_MAX)) ); } else if (index == uint(IStatController.ATTRIBUTES.DEXTERITY)) { attributes.setInt32(uint(IStatController.ATTRIBUTES.ATTACK_RATING), StatLib.attackRating(base, heroClass) + bonusMain.getInt32(uint(IStatController.ATTRIBUTES.ATTACK_RATING)) + bonusExtra.getInt32(uint(IStatController.ATTRIBUTES.ATTACK_RATING)) ); attributes.setInt32(uint(IStatController.ATTRIBUTES.DEFENSE), StatLib.defense(base, heroClass) + bonusMain.getInt32(uint(IStatController.ATTRIBUTES.DEFENSE)) + bonusExtra.getInt32(uint(IStatController.ATTRIBUTES.DEFENSE)) ); attributes.setInt32(uint(IStatController.ATTRIBUTES.BLOCK_RATING), StatLib.blockRating(base, heroClass) + bonusMain.getInt32(uint(IStatController.ATTRIBUTES.BLOCK_RATING)) + bonusExtra.getInt32(uint(IStatController.ATTRIBUTES.BLOCK_RATING)) ); } else if (index == uint(IStatController.ATTRIBUTES.VITALITY)) { attributes.setInt32(uint(IStatController.ATTRIBUTES.LIFE), StatLib.life(base, heroClass, _heroStats.level) + bonusMain.getInt32(uint(IStatController.ATTRIBUTES.LIFE)) + bonusExtra.getInt32(uint(IStatController.ATTRIBUTES.LIFE)) ); } else if (index == uint(IStatController.ATTRIBUTES.ENERGY)) { attributes.setInt32(uint(IStatController.ATTRIBUTES.MANA), StatLib.mana(base, heroClass, _heroStats.level) + bonusMain.getInt32(uint(IStatController.ATTRIBUTES.MANA)) + bonusExtra.getInt32(uint(IStatController.ATTRIBUTES.MANA)) ); } } function attributesAdd(int32[] memory base, int32[] memory add) internal pure returns (int32[] memory) { unchecked{ for (uint i; i < base.length; ++i) { base[i] += add[i]; } } return base; } // Currently this function is not used // function attributesRemove(int32[] memory base, int32[] memory remove) internal pure returns (int32[] memory) { // unchecked{ // for (uint i; i < base.length; ++i) { // base[i] = CalcLib.minusWithMinFloorI32(base[i], remove[i]); // } // } // return base; // } function packChangeableStats(IStatController.ChangeableStats memory stats) internal pure returns (bytes32) { uint32[] memory cData = new uint32[](5); cData[0] = stats.level; cData[1] = stats.experience; cData[2] = stats.life; cData[3] = stats.mana; cData[4] = stats.lifeChances; return cData.packUint32Array(); } function unpackChangeableStats(bytes32 data) internal pure returns (IStatController.ChangeableStats memory result) { uint32[] memory cData = data.unpackUint32Array(); return IStatController.ChangeableStats({ level: cData[0], experience: cData[1], life: cData[2], mana: cData[3], lifeChances: cData[4] }); } function bytesToFullAttributesArray(bytes32[] memory attributes) internal pure returns (int32[] memory result) { (int32[] memory values, uint8[] memory ids) = attributes.toInt32ArrayWithIds(); return valuesToFullAttributesArray(values, ids); } function valuesToFullAttributesArray(int32[] memory values, uint8[] memory ids) internal pure returns (int32[] memory result) { result = new int32[](uint(IStatController.ATTRIBUTES.END_SLOT)); for (uint i; i < values.length; ++i) { int32 value = values[i]; if (value != 0) { result[ids[i]] = value; } } } //endregion --------------------------- CALCULATIONS }
// 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; } }
// 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; } }
// 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; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Library for generating pseudorandom numbers. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibPRNG.sol) /// @author LazyShuffler based on NextShuffler by aschlosberg (divergencearran) /// (https://github.com/divergencetech/ethier/blob/main/contracts/random/NextShuffler.sol) library LibPRNG { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The initial length must be greater than zero and less than `2**32 - 1`. error InvalidInitialLazyShufflerLength(); /// @dev The new length must not be less than the current length. error InvalidNewLazyShufflerLength(); /// @dev The lazy shuffler has not been initialized. error LazyShufflerNotInitialized(); /// @dev Cannot double initialize the lazy shuffler. error LazyShufflerAlreadyInitialized(); /// @dev The lazy shuffle has finished. error LazyShuffleFinished(); /// @dev The queried index is out of bounds. error LazyShufflerGetOutOfBounds(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The scalar of ETH and most ERC20s. uint256 internal constant WAD = 1e18; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STRUCTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev A pseudorandom number state in memory. struct PRNG { uint256 state; } /// @dev A lazy Fisher-Yates shuffler for a range `[0..n)` in storage. struct LazyShuffler { // Bits Layout: // - [0..31] `numShuffled` // - [32..223] `permutationSlot` // - [224..255] `length` uint256 _state; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Seeds the `prng` with `state`. function seed(PRNG memory prng, uint256 state) internal pure { /// @solidity memory-safe-assembly assembly { mstore(prng, state) } } /// @dev Returns the next pseudorandom uint256. /// All bits of the returned uint256 pass the NIST Statistical Test Suite. function next(PRNG memory prng) internal pure returns (uint256 result) { // We simply use `keccak256` for a great balance between // runtime gas costs, bytecode size, and statistical properties. // // A high-quality LCG with a 32-byte state // is only about 30% more gas efficient during runtime, // but requires a 32-byte multiplier, which can cause bytecode bloat // when this function is inlined. // // Using this method is about 2x more efficient than // `nextRandomness = uint256(keccak256(abi.encode(randomness)))`. /// @solidity memory-safe-assembly assembly { result := keccak256(prng, 0x20) mstore(prng, result) } } /// @dev Returns a pseudorandom uint256, uniformly distributed /// between 0 (inclusive) and `upper` (exclusive). /// If your modulus is big, this method is recommended /// for uniform sampling to avoid modulo bias. /// For uniform sampling across all uint256 values, /// or for small enough moduli such that the bias is neligible, /// use {next} instead. function uniform(PRNG memory prng, uint256 upper) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { for {} 1 {} { result := keccak256(prng, 0x20) mstore(prng, result) if iszero(lt(result, mod(sub(0, upper), upper))) { break } } result := mod(result, upper) } } /// @dev Shuffles the array in-place with Fisher-Yates shuffle. function shuffle(PRNG memory prng, uint256[] memory a) internal pure { /// @solidity memory-safe-assembly assembly { let n := mload(a) let w := not(0) let mask := shr(128, w) if n { for { a := add(a, 0x20) } 1 {} { // We can just directly use `keccak256`, cuz // the other approaches don't save much. let r := keccak256(prng, 0x20) mstore(prng, r) // Note that there will be a very tiny modulo bias // if the length of the array is not a power of 2. // For all practical purposes, it is negligible // and will not be a fairness or security concern. { let j := add(a, shl(5, mod(shr(128, r), n))) n := add(n, w) // `sub(n, 1)`. if iszero(n) { break } let i := add(a, shl(5, n)) let t := mload(i) mstore(i, mload(j)) mstore(j, t) } { let j := add(a, shl(5, mod(and(r, mask), n))) n := add(n, w) // `sub(n, 1)`. if iszero(n) { break } let i := add(a, shl(5, n)) let t := mload(i) mstore(i, mload(j)) mstore(j, t) } } } } } /// @dev Shuffles the bytes in-place with Fisher-Yates shuffle. function shuffle(PRNG memory prng, bytes memory a) internal pure { /// @solidity memory-safe-assembly assembly { let n := mload(a) let w := not(0) let mask := shr(128, w) if n { let b := add(a, 0x01) for { a := add(a, 0x20) } 1 {} { // We can just directly use `keccak256`, cuz // the other approaches don't save much. let r := keccak256(prng, 0x20) mstore(prng, r) // Note that there will be a very tiny modulo bias // if the length of the array is not a power of 2. // For all practical purposes, it is negligible // and will not be a fairness or security concern. { let o := mod(shr(128, r), n) n := add(n, w) // `sub(n, 1)`. if iszero(n) { break } let t := mload(add(b, n)) mstore8(add(a, n), mload(add(b, o))) mstore8(add(a, o), t) } { let o := mod(and(r, mask), n) n := add(n, w) // `sub(n, 1)`. if iszero(n) { break } let t := mload(add(b, n)) mstore8(add(a, n), mload(add(b, o))) mstore8(add(a, o), t) } } } } } /// @dev Returns a sample from the standard normal distribution denominated in `WAD`. function standardNormalWad(PRNG memory prng) internal pure returns (int256 result) { /// @solidity memory-safe-assembly assembly { // Technically, this is the Irwin-Hall distribution with 20 samples. // The chance of drawing a sample outside 10 σ from the standard normal distribution // is ≈ 0.000000000000000000000015, which is insignificant for most practical purposes. // Passes the Kolmogorov-Smirnov test for 200k samples. Uses about 322 gas. result := keccak256(prng, 0x20) mstore(prng, result) let n := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff43 // Prime. let a := 0x100000000000000000000000000000051 // Prime and a primitive root of `n`. let m := 0x1fffffffffffffff1fffffffffffffff1fffffffffffffff1fffffffffffffff let s := 0x1000000000000000100000000000000010000000000000001 let r1 := mulmod(result, a, n) let r2 := mulmod(r1, a, n) let r3 := mulmod(r2, a, n) // forgefmt: disable-next-item result := sub(sar(96, mul(26614938895861601847173011183, add(add(shr(192, mul(s, add(and(m, result), and(m, r1)))), shr(192, mul(s, add(and(m, r2), and(m, r3))))), shr(192, mul(s, and(m, mulmod(r3, a, n))))))), 7745966692414833770) } } /// @dev Returns a sample from the unit exponential distribution denominated in `WAD`. function exponentialWad(PRNG memory prng) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { // Passes the Kolmogorov-Smirnov test for 200k samples. // Gas usage varies, starting from about 172+ gas. let r := keccak256(prng, 0x20) mstore(prng, r) let p := shl(129, r) let w := shl(1, r) if iszero(gt(w, p)) { let n := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff43 // Prime. let a := 0x100000000000000000000000000000051 // Prime and a primitive root of `n`. for {} 1 {} { r := mulmod(r, a, n) if iszero(lt(shl(129, r), w)) { r := mulmod(r, a, n) result := add(1000000000000000000, result) w := shl(1, r) p := shl(129, r) if iszero(lt(w, p)) { break } continue } w := shl(1, r) if iszero(lt(w, shl(129, r))) { break } } } result := add(div(p, shl(129, 170141183460469231732)), result) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE-BASED RANGE LAZY SHUFFLING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Initializes the state for lazy-shuffling the range `[0..n)`. /// Reverts if `n == 0 || n >= 2**32 - 1`. /// Reverts if `$` has already been initialized. /// If you need to reduce the length after initialization, just use a fresh new `$`. function initialize(LazyShuffler storage $, uint256 n) internal { /// @solidity memory-safe-assembly assembly { if iszero(lt(sub(n, 1), 0xfffffffe)) { mstore(0x00, 0x83b53941) // `InvalidInitialLazyShufflerLength()`. revert(0x1c, 0x04) } if sload($.slot) { mstore(0x00, 0x0c9f11f2) // `LazyShufflerAlreadyInitialized()`. revert(0x1c, 0x04) } mstore(0x00, $.slot) sstore($.slot, or(shl(224, n), shl(32, shr(64, keccak256(0x00, 0x20))))) } } /// @dev Increases the length of `$`. /// Reverts if `$` has not been initialized. function grow(LazyShuffler storage $, uint256 n) internal { /// @solidity memory-safe-assembly assembly { let state := sload($.slot) // The packed value at `$`. // If the new length is smaller than the old length, revert. if lt(n, shr(224, state)) { mstore(0x00, 0xbed37c6e) // `InvalidNewLazyShufflerLength()`. revert(0x1c, 0x04) } if iszero(state) { mstore(0x00, 0x1ead2566) // `LazyShufflerNotInitialized()`. revert(0x1c, 0x04) } sstore($.slot, or(shl(224, n), shr(32, shl(32, state)))) } } /// @dev Restarts the shuffler by setting `numShuffled` to zero, /// such that all elements can be drawn again. /// Restarting does NOT clear the internal permutation, nor changes the length. /// Even with the same sequence of randomness, reshuffling can yield different results. function restart(LazyShuffler storage $) internal { /// @solidity memory-safe-assembly assembly { let state := sload($.slot) if iszero(state) { mstore(0x00, 0x1ead2566) // `LazyShufflerNotInitialized()`. revert(0x1c, 0x04) } sstore($.slot, shl(32, shr(32, state))) } } /// @dev Returns the number of elements that have been shuffled. function numShuffled(LazyShuffler storage $) internal view returns (uint256 result) { /// @solidity memory-safe-assembly assembly { result := and(0xffffffff, sload($.slot)) } } /// @dev Returns the length of `$`. /// Returns zero if `$` is not initialized, else a non-zero value less than `2**32 - 1`. function length(LazyShuffler storage $) internal view returns (uint256 result) { /// @solidity memory-safe-assembly assembly { result := shr(224, sload($.slot)) } } /// @dev Returns if `$` has been initialized. function initialized(LazyShuffler storage $) internal view returns (bool result) { /// @solidity memory-safe-assembly assembly { result := iszero(iszero(sload($.slot))) } } /// @dev Returns if there are any more elements left to shuffle. /// Reverts if `$` is not initialized. function finished(LazyShuffler storage $) internal view returns (bool result) { /// @solidity memory-safe-assembly assembly { let state := sload($.slot) // The packed value at `$`. if iszero(state) { mstore(0x00, 0x1ead2566) // `LazyShufflerNotInitialized()`. revert(0x1c, 0x04) } result := eq(shr(224, state), and(0xffffffff, state)) } } /// @dev Returns the current value stored at `index`, accounting for all historical shuffling. /// Reverts if `index` is greater than or equal to the `length` of `$`. function get(LazyShuffler storage $, uint256 index) internal view returns (uint256 result) { /// @solidity memory-safe-assembly assembly { let state := sload($.slot) // The packed value at `$`. let n := shr(224, state) // Length of `$`. if iszero(lt(index, n)) { mstore(0x00, 0x61367cc4) // `LazyShufflerGetOutOfBounds()`. revert(0x1c, 0x04) } let u32 := gt(n, 0xfffe) let s := add(shr(sub(4, u32), index), shr(64, shl(32, state))) // Bucket slot. let o := shl(add(4, u32), and(index, shr(u32, 15))) // Bucket slot offset (bits). let m := sub(shl(shl(u32, 16), 1), 1) // Value mask. result := and(m, shr(o, sload(s))) result := xor(index, mul(xor(index, sub(result, 1)), iszero(iszero(result)))) } } /// @dev Does a single Fisher-Yates shuffle step, increments the `numShuffled` in `$`, /// and returns the next value in the shuffled range. /// `randomness` can be taken from a good-enough source, or a higher quality source like VRF. /// Reverts if there are no more values to shuffle, which includes the case if `$` is not initialized. function next(LazyShuffler storage $, uint256 randomness) internal returns (uint256 chosen) { /// @solidity memory-safe-assembly assembly { function _get(u32_, state_, i_) -> _value { let s_ := add(shr(sub(4, u32_), i_), shr(64, shl(32, state_))) // Bucket slot. let o_ := shl(add(4, u32_), and(i_, shr(u32_, 15))) // Bucket slot offset (bits). let m_ := sub(shl(shl(u32_, 16), 1), 1) // Value mask. _value := and(m_, shr(o_, sload(s_))) _value := xor(i_, mul(xor(i_, sub(_value, 1)), iszero(iszero(_value)))) } function _set(u32_, state_, i_, value_) { let s_ := add(shr(sub(4, u32_), i_), shr(64, shl(32, state_))) // Bucket slot. let o_ := shl(add(4, u32_), and(i_, shr(u32_, 15))) // Bucket slot offset (bits). let m_ := sub(shl(shl(u32_, 16), 1), 1) // Value mask. let v_ := sload(s_) // Bucket slot value. value_ := mul(iszero(eq(i_, value_)), add(value_, 1)) sstore(s_, xor(v_, shl(o_, and(m_, xor(shr(o_, v_), value_))))) } let state := sload($.slot) // The packed value at `$`. let shuffled := and(0xffffffff, state) // Number of elements shuffled. let n := shr(224, state) // Length of `$`. let remainder := sub(n, shuffled) // Number of elements left to shuffle. if iszero(remainder) { mstore(0x00, 0x51065f79) // `LazyShuffleFinished()`. revert(0x1c, 0x04) } mstore(0x00, randomness) // (Re)hash the randomness so that we don't mstore(0x20, shuffled) // need to expect guarantees on its distribution. let index := add(mod(keccak256(0x00, 0x40), remainder), shuffled) chosen := _get(gt(n, 0xfffe), state, index) _set(gt(n, 0xfffe), state, index, _get(gt(n, 0xfffe), state, shuffled)) _set(gt(n, 0xfffe), state, shuffled, chosen) sstore($.slot, add(1, state)) // Increment the `numShuffled` by 1, and store it. } } }
{ "evmVersion": "istanbul", "optimizer": { "enabled": true, "runs": 50 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"name":"IncompatibleInputString","type":"error"},{"inputs":[{"internalType":"int256","name":"value","type":"int256"}],"name":"IntOutOfRange","type":"error"},{"inputs":[],"name":"LengthsMismatch","type":"error"},{"inputs":[{"internalType":"uint256","name":"pageId","type":"uint256"}],"name":"PageNotRemovedError","type":"error"},{"inputs":[],"name":"ZeroStoryIdRemoveStory","type":"error"},{"inputs":[],"name":"ZeroValue","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"storyId","type":"uint256"},{"components":[{"internalType":"uint16[]","name":"pageId","type":"uint16[]"},{"internalType":"uint8[]","name":"heroClass","type":"uint8[]"},{"internalType":"uint16[]","name":"answerId","type":"uint16[]"},{"internalType":"bool[][]","name":"cores","type":"bool[][]"},{"internalType":"uint8[][]","name":"ids","type":"uint8[][]"},{"internalType":"int32[][]","name":"values","type":"int32[][]"}],"indexed":false,"internalType":"struct IStoryController.AnswerAttributeRequirementsMeta","name":"meta","type":"tuple"}],"name":"SetAnswerAttributeRequirements","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"storyId","type":"uint256"},{"components":[{"internalType":"uint16[]","name":"pageId","type":"uint16[]"},{"internalType":"uint8[]","name":"heroClass","type":"uint8[]"},{"internalType":"uint16[]","name":"answerId","type":"uint16[]"},{"internalType":"uint32[]","name":"randomRequirements","type":"uint32[]"},{"internalType":"uint32[]","name":"delayRequirements","type":"uint32[]"},{"internalType":"bool[]","name":"isFinalAnswer","type":"bool[]"}],"indexed":false,"internalType":"struct IStoryController.AnswerAttributesMeta","name":"meta","type":"tuple"}],"name":"SetAnswerAttributes","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"storyId","type":"uint256"},{"components":[{"internalType":"uint16[]","name":"pageId","type":"uint16[]"},{"internalType":"uint8[]","name":"heroClass","type":"uint8[]"},{"internalType":"uint16[]","name":"answerId","type":"uint16[]"},{"internalType":"bytes32[][]","name":"dataIndexes","type":"bytes32[][]"},{"internalType":"bool[][]","name":"mandatory","type":"bool[][]"},{"internalType":"uint64[][]","name":"dataValuesMin","type":"uint64[][]"},{"internalType":"uint64[][]","name":"dataValuesMax","type":"uint64[][]"}],"indexed":false,"internalType":"struct IStoryController.AnswerCustomDataMeta","name":"meta","type":"tuple"}],"name":"SetAnswerGlobalCustomDataRequirementMeta","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"storyId","type":"uint256"},{"components":[{"internalType":"uint16[]","name":"pageId","type":"uint16[]"},{"internalType":"uint8[]","name":"heroClass","type":"uint8[]"},{"internalType":"uint16[]","name":"answerId","type":"uint16[]"},{"internalType":"bytes32[][]","name":"dataIndexes","type":"bytes32[][]"},{"internalType":"bool[][]","name":"mandatory","type":"bool[][]"},{"internalType":"uint64[][]","name":"dataValuesMin","type":"uint64[][]"},{"internalType":"uint64[][]","name":"dataValuesMax","type":"uint64[][]"}],"indexed":false,"internalType":"struct IStoryController.AnswerCustomDataMeta","name":"meta","type":"tuple"}],"name":"SetAnswerHeroCustomDataRequirementMeta","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"storyId","type":"uint256"},{"components":[{"internalType":"uint16[]","name":"pageId","type":"uint16[]"},{"internalType":"uint8[]","name":"heroClass","type":"uint8[]"},{"internalType":"uint16[]","name":"answerId","type":"uint16[]"},{"internalType":"address[][]","name":"requireItems","type":"address[][]"},{"internalType":"bool[][]","name":"requireItemBurn","type":"bool[][]"},{"internalType":"bool[][]","name":"requireItemEquipped","type":"bool[][]"}],"indexed":false,"internalType":"struct IStoryController.AnswerItemRequirementsMeta","name":"meta","type":"tuple"}],"name":"SetAnswerItemRequirements","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"storyId","type":"uint256"},{"components":[{"internalType":"uint16[]","name":"pageId","type":"uint16[]"},{"internalType":"uint8[]","name":"heroClass","type":"uint8[]"},{"internalType":"uint16[]","name":"answerId","type":"uint16[]"},{"internalType":"uint8[]","name":"answerResultIds","type":"uint8[]"},{"internalType":"uint16[][]","name":"answerNextPageIds","type":"uint16[][]"}],"indexed":false,"internalType":"struct IStoryController.AnswerNextPageMeta","name":"meta","type":"tuple"}],"name":"SetAnswerNextPageMeta","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"storyId","type":"uint256"},{"components":[{"internalType":"uint16[]","name":"pageId","type":"uint16[]"},{"internalType":"uint8[]","name":"heroClass","type":"uint8[]"},{"internalType":"uint16[]","name":"answerId","type":"uint16[]"},{"internalType":"address[][]","name":"requireToken","type":"address[][]"},{"internalType":"uint88[][]","name":"requireAmount","type":"uint88[][]"},{"internalType":"bool[][]","name":"requireTransfer","type":"bool[][]"}],"indexed":false,"internalType":"struct IStoryController.AnswerTokenRequirementsMeta","name":"meta","type":"tuple"}],"name":"SetAnswerTokenRequirementsMeta","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"storyId","type":"uint256"},{"indexed":false,"internalType":"uint16[]","name":"answerPageIds","type":"uint16[]"},{"indexed":false,"internalType":"uint8[]","name":"answerHeroClasses","type":"uint8[]"},{"indexed":false,"internalType":"uint16[]","name":"answerIds","type":"uint16[]"}],"name":"SetAnswersMeta","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"storyId","type":"uint256"},{"components":[{"internalType":"uint16[]","name":"pageId","type":"uint16[]"},{"internalType":"uint8[]","name":"heroClass","type":"uint8[]"},{"internalType":"uint16[]","name":"answerId","type":"uint16[]"},{"internalType":"uint8[][]","name":"slots","type":"uint8[][]"},{"internalType":"uint64[][]","name":"chances","type":"uint64[][]"},{"internalType":"bool[][]","name":"isStopIfBurnt","type":"bool[][]"}],"indexed":false,"internalType":"struct IStoryController.AnswerBurnRandomItemMeta","name":"meta","type":"tuple"}],"name":"SetBurnItemsMeta","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"storyId","type":"uint256"},{"components":[{"internalType":"uint16[]","name":"pageId","type":"uint16[]"},{"internalType":"uint8[]","name":"heroClass","type":"uint8[]"},{"internalType":"uint16[]","name":"answerId","type":"uint16[]"},{"internalType":"bytes32[][]","name":"dataIndexes","type":"bytes32[][]"},{"internalType":"int16[][]","name":"dataValues","type":"int16[][]"}],"indexed":false,"internalType":"struct IStoryController.AnswerCustomDataResultMeta","name":"meta","type":"tuple"},{"indexed":false,"internalType":"enum IStoryController.CustomDataResult","name":"_type","type":"uint8"}],"name":"SetCustomDataResult","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"storyId","type":"uint256"},{"components":[{"internalType":"uint16[]","name":"pageId","type":"uint16[]"},{"internalType":"uint8[]","name":"heroClass","type":"uint8[]"},{"internalType":"uint16[]","name":"answerId","type":"uint16[]"},{"internalType":"uint8[][]","name":"attributeIds","type":"uint8[][]"},{"internalType":"int32[][]","name":"attributeValues","type":"int32[][]"},{"internalType":"uint32[]","name":"experience","type":"uint32[]"},{"internalType":"int32[]","name":"heal","type":"int32[]"},{"internalType":"int32[]","name":"manaRegen","type":"int32[]"},{"internalType":"int32[]","name":"lifeChancesRecovered","type":"int32[]"},{"internalType":"int32[]","name":"damage","type":"int32[]"},{"internalType":"int32[]","name":"manaConsumed","type":"int32[]"},{"internalType":"address[][]","name":"mintItems","type":"address[][]"},{"internalType":"uint32[][]","name":"mintItemsChances","type":"uint32[][]"}],"indexed":false,"internalType":"struct IStoryController.AnswerResultMeta","name":"meta","type":"tuple"}],"name":"SetFailInfo","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"storyId","type":"uint256"},{"components":[{"internalType":"uint16[]","name":"nextObjPageIds","type":"uint16[]"},{"internalType":"uint8[]","name":"nextObjHeroClasses","type":"uint8[]"},{"internalType":"uint32[][]","name":"nextObjIds","type":"uint32[][]"}],"indexed":false,"internalType":"struct IStoryController.NextObjRewriteMeta","name":"meta","type":"tuple"}],"name":"SetNextObjRewriteMeta","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"storyId","type":"uint256"},{"components":[{"internalType":"uint16[]","name":"pageId","type":"uint16[]"},{"internalType":"uint8[]","name":"heroClass","type":"uint8[]"},{"internalType":"uint16[]","name":"answerId","type":"uint16[]"},{"internalType":"uint8[][]","name":"attributeIds","type":"uint8[][]"},{"internalType":"int32[][]","name":"attributeValues","type":"int32[][]"},{"internalType":"uint32[]","name":"experience","type":"uint32[]"},{"internalType":"int32[]","name":"heal","type":"int32[]"},{"internalType":"int32[]","name":"manaRegen","type":"int32[]"},{"internalType":"int32[]","name":"lifeChancesRecovered","type":"int32[]"},{"internalType":"int32[]","name":"damage","type":"int32[]"},{"internalType":"int32[]","name":"manaConsumed","type":"int32[]"},{"internalType":"address[][]","name":"mintItems","type":"address[][]"},{"internalType":"uint32[][]","name":"mintItemsChances","type":"uint32[][]"}],"indexed":false,"internalType":"struct IStoryController.AnswerResultMeta","name":"meta","type":"tuple"}],"name":"SetSuccessInfo","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"storyId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"requiredCustomDataIndex","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"requiredCustomDataMinValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"requiredCustomDataMaxValue","type":"uint256"},{"indexed":false,"internalType":"bool","name":"requiredCustomDataIsHero","type":"bool"}],"name":"StoryCustomDataRequirements","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"objectId","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"storyId","type":"uint256"}],"name":"StoryFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"objectId","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"storyId","type":"uint256"}],"name":"StoryRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"storyId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"requiredLevel","type":"uint256"}],"name":"StoryRequiredLevel","type":"event"}]
Contract Creation Code
6157aa6200003b600b82828239805160001a60731461002e57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600436106101165760003560e01c80639c9dc579116100a75780639c9dc5791461025d578063a84d660c1461027d578063a913674b1461029d578063b5083ab9146102bd578063b5287c91146102dd578063c258bf40146102fd578063c5828ff11461031d578063ca4739361461033d578063cbbefc281461035d578063d335b7f31461037d57600080fd5b806301c3192f1461011b57806301c7664c1461013d5780630324a18c1461015d5780631c9a90081461017d5780633e8dd8681461019d57806356814190146101bd578063679dde4c146101dd5780636b6cc9f8146101fd578063751dda431461021d5780638cd5642c1461023d575b600080fd5b81801561012757600080fd5b5061013b610136366004612d92565b61039d565b005b81801561014957600080fd5b5061013b61015836600461321a565b6104f9565b81801561016957600080fd5b5061013b610178366004613440565b610546565b81801561018957600080fd5b5061013b6101983660046134c1565b610757565b8180156101a957600080fd5b5061013b6101b8366004613505565b610808565b8180156101c957600080fd5b5061013b6101d8366004613814565b610be5565b8180156101e957600080fd5b5061013b6101f8366004613930565b610db4565b81801561020957600080fd5b5061013b610218366004613ad8565b610f47565b81801561022957600080fd5b5061013b610238366004613f20565b611048565b81801561024957600080fd5b5061013b6102583660046140bc565b611090565b81801561026957600080fd5b5061013b6102783660046143e8565b6111dd565b81801561028957600080fd5b5061013b610298366004614744565b61135d565b8180156102a957600080fd5b5061013b6102b8366004614790565b6114ea565b8180156102c957600080fd5b5061013b6102d8366004613f20565b611621565b8180156102e957600080fd5b5061013b6102f8366004613505565b611669565b81801561030957600080fd5b5061013b6103183660046147bc565b611880565b81801561032957600080fd5b5061013b61033836600461482e565b611c21565b81801561034957600080fd5b5061013b61035836600461487a565b611dae565b81801561036957600080fd5b5061013b61037836600461321a565b611fbb565b81801561038957600080fd5b5061013b61039836600461494a565b611ffb565b825160005b818110156104b3576103ce87878784815181106103c1576103c1614996565b60200260200101516120fe565b60008760060160006104218885815181106103eb576103eb614996565b602002602001015188868151811061040557610405614996565b60200260200101518b61ffff166121259092919063ffffffff16565b81526020019081526020016000209050600061048b898989868151811061044a5761044a614996565b602002602001015189878151811061046457610464614996565b602002602001015189888151811061047e5761047e614996565b6020026020010151612149565b905080156104a9578154600181018355600083815260209020018190555b50506001016103a2565b50507f49007bba987368aede38546653f41bd86c6ed04d5e7ba4ca166432c014b2138c848484846040516104ea9493929190614a20565b60405180910390a15050505050565b61050883838386600d0161218d565b7f49126b5a176699b1ec3b1eac8c744bfaf006d2cefcd10f794c25f29a3ace0b8e8282604051610539929190614be7565b60405180910390a1505050565b81515160005b81811015610714576000801b6105af87878760000151858151811061057357610573614996565b60200260200101518860200151868151811061059157610591614996565b60200260200101518960400151878151811061047e5761047e614996565b1461070c576000610633856000015183815181106105cf576105cf614996565b6020026020010151866020015184815181106105ed576105ed614996565b60200260200101518760400151858151811061060b5761060b614996565b602002602001015187600581111561062557610625614cc9565b61ffff8b1693929190612318565b600081815260158901602052604081209192505b8660600151848151811061065d5761065d614996565b60200260200101515181101561070857816106ed8860800151868151811061068757610687614996565b602002602001015183815181106106a0576106a0614996565b6020026020010151896060015187815181106106be576106be614996565b602002602001015184815181106106d7576106d7614996565b602002602001015161235a90919063ffffffff16565b81546001818101845560009384526020909320015501610647565b5050505b60010161054c565b50507f5144a9cbb149e306e7330f55e21a6b72086d1b86b4aef7a9d76713048802295183838360405161074993929190614d01565b60405180910390a150505050565b63ffffffff8216600081815260038601602090815260408083208054600160ff19918216811790925561ffff891680865260028b01855283862080549092168317909155858552898452828520805461ffff1916821790558085529089018352818420805463ffffffff191686179055601a89018352928190208590558051938452908301919091527f77b037a0afbc269c250968ad8197fbc330f78621131eb33de9de913bc8a510d69101610749565b6108106128c6565b61ffff831660608201819052600090815260058501602052604090206108358161238a565b60c0830181905283101561084b5760c082018390525b8160c001516001600160401b0381111561086757610867612b70565b604051908082528060200260200182016040528015610890578160200160208202803683370190505b5061012083015260005b8260c00151811015610b91576108b0828261239a565b61010084018190526101208401518051839081106108d0576108d0614996565b6020026020010181815250506108fa83610100015190601082901c90602083901c90602884901c90565b61ffff90811660a088015260ff909116865216608085015250610100830180516000908152600988016020908152604080832083905592518252600a890190529081206109469161291c565b6101008301516000908152600b8701602052604081206109659161291c565b6101008301516000908152600c8701602052604081206109849161291c565b6101008301516000908152600d8701602052604081206109a39161293d565b6101008301516000908152600e8701602052604081206109c29161293d565b6101008301516000908152600f8701602052604081206109e19161291c565b610100830180516000908152601088016020908152604080832083905592518252601189019052908120610a149161291c565b61010083015160009081526012870160205260408120610a339161291c565b610100830180516000908152601388016020908152604080832083905592518252601489019052908120610a669161291c565b61010083015160009081526016870160205260408120610a859161291c565b600060208401525b6007836020015160ff161015610b0757856007016000610ad4856080015186600001518760a001518860200151896060015161ffff1661231890949392919063ffffffff16565b81526020019081526020016000206000610aee919061295e565b826020018051610afd90614e3a565b60ff169052610a8d565b600160408401525b6005836040015160ff161015610b8957856015016000610b56856080015186600001518760a001518860400151896060015161ffff1661231890949392919063ffffffff16565b81526020019081526020016000206000610b70919061291c565b826040018051610b7f90614e3a565b60ff169052610b0f565b60010161089a565b5060005b82610120015151811015610bdd57610bd48361012001518281518110610bbd57610bbd614996565b6020026020010151836123ad90919063ffffffff16565b50600101610b95565b505050505050565b80515160005b81811015610d81576000610c4c868686600001518581518110610c1057610c10614996565b602002602001015187602001518681518110610c2e57610c2e614996565b60200260200101518860400151878151811061047e5761047e614996565b90508015610d78576000818152600c870160205260408120905b85606001518481518110610c7c57610c7c614996565b602002602001015151811015610d75576000610d4e87608001518681518110610ca757610ca7614996565b60200260200101518381518110610cc057610cc0614996565b60200260200101518860a001518781518110610cde57610cde614996565b60200260200101518481518110610cf757610cf7614996565b602002602001015189606001518881518110610d1557610d15614996565b60200260200101518581518110610d2e57610d2e614996565b60200260200101516001600160a01b03166123b99092919063ffffffff16565b90508015610d6c578254600181018455600084815260209020018190555b50600101610c66565b50505b50600101610beb565b50507ff7763619246d8f0933bc30f26ed64aa852419d4c03682ebe62c07b2b5e58189a8282604051610539929190614ed3565b80515160005b81811015610f14576000610ddf868686600001518581518110610c1057610c10614996565b90508015610f0b576000818152600b870160205260408120905b85606001518481518110610e0f57610e0f614996565b602002602001015151811015610f08576000610ee187608001518681518110610e3a57610e3a614996565b60200260200101518381518110610e5357610e53614996565b60200260200101518860a001518781518110610e7157610e71614996565b60200260200101518481518110610e8a57610e8a614996565b602002602001015189606001518881518110610ea857610ea8614996565b60200260200101518581518110610ec157610ec1614996565b60200260200101516001600160a01b03166123f39092919063ffffffff16565b90508015610eff578254600181018455600084815260209020018190555b50600101610df9565b50505b50600101610dba565b50507f75e2a553fd5c4396591939214dccd44f121aa91d9419cb35347afa55e0335bab8282604051610539929190615006565b80515160005b81811015611015576000610f72868686600001518581518110610c1057610c10614996565b9050801561100c576000610fee85608001518481518110610f9557610f95614996565b60200260200101518660a001518581518110610fb357610fb3614996565b602002602001015187606001518681518110610fd157610fd1614996565b602002602001015163ffffffff166124229092919063ffffffff16565b9050801561100a57600082815260098801602052604090208190555b505b50600101610f4d565b50507fee4f8e4fe9011c317080a888f3ea5fd4984b1f407ba1eeb36840cdb64ceef48982826040516105399291906150f7565b61105f83838386600f018760100188601101611880565b7f869c57977aa939d74e2da756cb2e738c080b4231a44f4cd81950529f2ff6bbdd82826040516105399291906152cc565b80515160005b818110156111aa5760006110bb868686600001518581518110610c1057610c10614996565b905080156111a1576000611156856000015184815181106110de576110de614996565b6020026020010151866020015185815181106110fc576110fc614996565b60200260200101518760400151868151811061111a5761111a614996565b60200260200101518860600151878151811061113857611138614996565b60200260200101518a61ffff1661231890949392919063ffffffff16565b90508460800151838151811061116e5761116e614996565b6020026020010151876007016000838152602001908152602001600020908051906020019061119e929190612983565b50505b50600101611096565b50507f978987b8c70f4ee959e5636ae6f9699d1e1473928283af297505ae4147f96ed88282604051610539929190615460565b6111f1828260000151836101c00151611c21565b611205828260000151836101e00151611ffb565b805160c08201518051602082015160409092015161122793869390929161039d565b61123a8282600001518360e00151611090565b61124e82826000015183610100015161135d565b611262828260000151836101200151610db4565b611276828260000151836101400151610be5565b61128a828260000151836101600151610f47565b61129e8282600001518361018001516104f9565b6112b2828260000151836101a00151611fbb565b6112c6828260000151836102000151611048565b6112da828260000151836102200151611621565b6112f08282600001518361024001516001610546565b6113068282600001518361026001516002610546565b61131c8282600001518361028001516003610546565b611332828260000151836102a001516004610546565b61135982826000015183602001518460400151856060015186608001518760a00151611dae565b5050565b80515160005b818110156114b7576000611388868686600001518581518110610c1057610c10614996565b905080156114ae576000818152600a870160205260408120905b856060015184815181106113b8576113b8614996565b6020026020010151518110156114ab5760006114848760a0015186815181106113e3576113e3614996565b602002602001015183815181106113fc576113fc614996565b60200260200101518860600151878151811061141a5761141a614996565b6020026020010151848151811061143357611433614996565b60200260200101518960800151888151811061145157611451614996565b6020026020010151858151811061146a5761146a614996565b602002602001015160ff166124469092919063ffffffff16565b905080156114a2578254600181018455600084815260209020018190555b506001016113a2565b50505b50600101611363565b50507f6ae02f4d7862466cf651e781506106bd39987973bcbd68688021b5bcfbd8f6848282604051610539929190615547565b63ffffffff811660009081526020839052604090205461ffff161580611528575063ffffffff8116600090815260038301602052604090205460ff16155b1561154657604051634f12d70760e11b815260040160405180910390fd5b63ffffffff8116600081815260208481526040808320805461ffff16808552600288018452828520805460ff19908116909155825461ffff1916909255600188018452828520805463ffffffff1916905594845260038701835281842080549091169055838352601a860182528083208390556017860190915281206115cb9161293d565b61ffff81166000818152601885016020908152604080832092909255815163ffffffff86168152908101929092527f36206a58dc94b3b1d63eaf20aff0d8b78da875c59739496e88c021ad18422a129101610539565b611638838383866012018760130188601401611880565b7f4b868e06a41dbd00f1781690c7c901b28487927ffb0dd540e9c643fda33232bb82826040516105399291906152cc565b6116716128c6565b61ffff831660608201819052600090815260048501602052604090206116968161238a565b60c083018190528310156116ac5760c082018390525b8160c001516001600160401b038111156116c8576116c8612b70565b6040519080825280602002602001820160405280156116f1578160200160208202803683370190505b5060e083015260005b8260c0015181101561180757611710828261239a565b8360e00151828151811061172657611726614996565b6020026020010181815250508260e00151818151811061174857611748614996565b602090810291909101015161ffff166080840152600083525b6007835160ff1610156117ff5760808301518351606085015160068901926000926117919261ffff169190612125565b815260200190815260200160002060006117ab919061291c565b60808301518351606085015160088901926000926117ce9261ffff169190612125565b815260200190815260200160002060006117e89190612a2c565b825183906117f590614e3a565b60ff169052611761565b6001016116fa565b5060005b8260e0015151811015610bdd576118318360e001518281518110610bbd57610bbd614996565b611878578260e00151818151811061184b5761184b614996565b6020026020010151604051632fd1568f60e11b815260040161186f91815260200190565b60405180910390fd5b60010161180b565b83515160005b81811015611c175760006118e78989896000015185815181106118ab576118ab614996565b60200260200101518a6020015186815181106118c9576118c9614996565b60200260200101518b60400151878151811061047e5761047e614996565b90508015611c0e578660600151828151811061190557611905614996565b602002602001015151600014611985576119628760600151838151811061192e5761192e614996565b60200260200101518860800151848151811061194c5761194c614996565b602002602001015161246590919063ffffffff16565b60008281526020888152604090912082516119839391929190910190612a51565b505b6000611aa48860a0015184815181106119a0576119a0614996565b60200260200101518960c0015185815181106119be576119be614996565b60200260200101518a60e0015186815181106119dc576119dc614996565b60200260200101518b610100015187815181106119fb576119fb614996565b60200260200101518c61012001518881518110611a1a57611a1a614996565b60200260200101518d61014001518981518110611a3957611a39614996565b60209081029190910181015163ffffffff9690961694901b63ffffffff60201b169390931760409290921b63ffffffff60401b169190911760609190911b63ffffffff60601b161760809190911b63ffffffff60801b161760a09190911b63ffffffff60a01b161790565b90508015611abe5760008281526020879052604090208190555b60008861016001518481518110611ad757611ad7614996565b602002602001015151905080600014611c0b576000816001600160401b03811115611b0457611b04612b70565b604051908082528060200260200182016040528015611b2d578160200160208202803683370190505b50905060005b82811015611bea57611bc58b61018001518781518110611b5557611b55614996565b60200260200101518281518110611b6e57611b6e614996565b60200260200101518c61016001518881518110611b8d57611b8d614996565b60200260200101518381518110611ba657611ba6614996565b60200260200101516001600160a01b03166126c390919063ffffffff16565b828281518110611bd757611bd7614996565b6020908102919091010152600101611b33565b506000848152602088815260409091208251611c0892840190612a51565b50505b50505b50600101611886565b5050505050505050565b80515160005b81811015611d7b576000611c4c868686600001518581518110610c1057610c10614996565b90508015611d725760008181526016870160205260408120905b85606001518481518110611c7c57611c7c614996565b602002602001015151811015611d6f576000611d4887608001518681518110611ca757611ca7614996565b60200260200101518381518110611cc057611cc0614996565b60200260200101518860a001518781518110611cde57611cde614996565b60200260200101518481518110611cf757611cf7614996565b602002602001015189606001518881518110611d1557611d15614996565b60200260200101518581518110611d2e57611d2e614996565b602002602001015160ff166126e19092919063ffffffff16565b90508015611d66578254600181018455600084815260209020018190555b50600101611c66565b50505b50600101611c27565b50507fe8aef7992dddcf8eab441423fa358ec12b14f62644161a4641efd65f5e5a8f0d8282604051610539929190615601565b61ffff86166000818152601889016020908152604091829020849055815192835282018390527f979e00d9ab95347fc5a2a8936556e51fbf30d23aa2b43e95a307f37590db590e910160405180910390a161ffff861660009081526017880160205260408120905b8651811015611fb057816040518060400160405280898481518110611e3d57611e3d614996565b60200260200101518152602001611eb3888581518110611e5f57611e5f614996565b6020026020010151888681518110611e7957611e79614996565b60200260200101518b8781518110611e9357611e93614996565b60200260200101516001600160401b03166127049092919063ffffffff16565b905281546001818101845560009384526020938490208351600290930201918255929091015191015586517fa4f88bdc8eedabd5953d882c5d392f683d96cbda2c76003a7f0340083bc6148b908990899084908110611f1457611f14614996565b6020026020010151888481518110611f2e57611f2e614996565b6020026020010151888581518110611f4857611f48614996565b6020026020010151888681518110611f6257611f62614996565b6020908102919091018101516040805161ffff9097168752918601949094526001600160401b03928316908501521660608301521515608082015260a00160405180910390a1600101611e16565b505050505050505050565b611fca83838386600e0161218d565b7f0d0f8f5e403cc74ac4909b11cbfe60cc6e48a9973ba32e10b9449a7365c34e688282604051610539929190614be7565b80515160005b818110156120cb576120248585856000015184815181106103c1576103c1614996565b60006120798460000151838151811061203f5761203f614996565b60200260200101518560200151848151811061205d5761205d614996565b60200260200101518761ffff166121259092919063ffffffff16565b90508360400151828151811061209157612091614996565b602002602001015186600801600083815260200190815260200160002090805190602001906120c1929190612a8c565b5050600101612001565b50507f3068977f27d58675414225648f9fddefc01a84758bcc359b64f244480b30f770828260405161053992919061569d565b61ffff8281166000908152600485016020526040902061211f91831661272f565b50505050565b61ffff9290921660109190911b63ffff0000161760209190911b60ff60201b161790565b61ffff8416601084901b63ffff00001617602083901b60ff60201b1617602882901b61ffff60281b161780156121845761218486868361273b565b95945050505050565b81515160005b81811015610bdd5760006121b887878760000151858151811061057357610573614996565b9050801561230f57600081815260208590526040812060608701518051919291859081106121e8576121e8614996565b6020026020010151905060008760800151858151811061220a5761220a614996565b6020026020010151905060008860a00151868151811061222c5761222c614996565b6020026020010151905060008960c00151878151811061224e5761224e614996565b6020026020010151905060005b84518110156123085785604051806040016040528087848151811061228257612282614996565b602002602001015181526020016122d88585815181106122a4576122a4614996565b60200260200101518886815181106122be576122be614996565b6020026020010151888781518110611e9357611e93614996565b9052815460018181018455600093845260209384902083516002909302019182559290910151908201550161225b565b5050505050505b50600101612193565b61ffff9490941660109390931b63ffff0000169290921760209190911b60ff60201b161760289190911b61ffff60281b161760389190911b60ff60381b161790565b600061ffff19831683146123815760405163508af79560e01b815260040160405180910390fd5b5061ffff161790565b6000612394825490565b92915050565b60006123a6838361275a565b9392505050565b60006123a68383612784565b600160a01b600160f81b0360a083901b166001600160a01b0384161760f8826123e35760006123e6565b60015b60ff16901b179392505050565b6001600160a01b03831660a08361240b57600061240e565b60015b60ff16901b1760a8826123e35760006123e6565b63ffffffff60201b602083901b1663ffffffff8416176040826123e35760006123e6565b64ffffffff00600883901b1660ff8416176028826123e35760006123e6565b606081518351146124895760405163586cb9e160e01b815260040160405180910390fd5b60006008845161249991906156ff565b6124a4906001615721565b90506000816001600160401b038111156124c0576124c0612b70565b6040519080825280602002602001820160405280156124e9578160200160208202803683370190505b50905060005b828110156126ba5760005b60088110156126b157600081612511846008615734565b61251b9190615721565b90508751811061252b57506126b1565b627fffff60020b88828151811061254457612544614996565b602002602001015160030b138061257d5750627fffff1960020b88828151811061257057612570614996565b602002602001015160030b125b156125bb5787818151811061259457612594614996565b602002602001015160030b604051632eb1d06960e01b815260040161186f91815260200190565b8781815181106125cd576125cd614996565b602002602001015160030b6000036125f857604051637c946ed760e01b815260040160405180910390fd5b612603826020615734565b88828151811061261557612615614996565b602002602001015162ffffff1660001b901b84848151811061263957612639614996565b6020026020010181815117915081815250508160206126589190615734565b612663906018615721565b87828151811061267557612675614996565b602002602001015160ff1660001b901b84848151811061269757612697614996565b6020908102919091010180519091179052506001016124fa565b506001016124ef565b50949350505050565b63ffffffff60a01b60a09190911b166001600160a01b039091161790565b68ffffffffffffffff00600883901b1660ff8416176048826123e35760006123e6565b67ffffffffffffffff60401b604083901b166001600160401b038416176080826123e35760006123e6565b60006123a68383612877565b61ffff82166000908152600584016020526040902061211f908261272f565b600082600001828154811061277157612771614996565b9060005260206000200154905092915050565b6000818152600183016020526040812054801561286d5760006127a860018361574b565b85549091506000906127bc9060019061574b565b90508082146128215760008660000182815481106127dc576127dc614996565b90600052602060002001549050808760000184815481106127ff576127ff614996565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806128325761283261575e565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050612394565b6000915050612394565b60008181526001830160205260408120546128be57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155612394565b506000612394565b604080516101408101825260008082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820183905260e0820181905261010082019290925261012081019190915290565b508054600082559060005260206000209081019061293a9190612b29565b50565b508054600082556002029060005260206000209081019061293a9190612b3e565b50805460008255600f01601090049060005260206000209081019061293a9190612b29565b82805482825590600052602060002090600f01601090048101928215612a1c5791602002820160005b838211156129ec57835183826101000a81548161ffff021916908361ffff16021790555092602001926002016020816001010492830192600103026129ac565b8015612a1a5782816101000a81549061ffff02191690556002016020816001010492830192600103026129ec565b505b50612a28929150612b29565b5090565b50805460008255600701600890049060005260206000209081019061293a9190612b29565b828054828255906000526020600020908101928215612a1c579160200282015b82811115612a1c578251825591602001919060010190612a71565b82805482825590600052602060002090600701600890048101928215612a1c5791602002820160005b83821115612af957835183826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302612ab5565b8015612a1a5782816101000a81549063ffffffff0219169055600401602081600301049283019260010302612af9565b5b80821115612a285760008155600101612b2a565b5b80821115612a285760008082556001820155600201612b3f565b803561ffff81168114612b6b57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60405160e081016001600160401b0381118282101715612ba857612ba8612b70565b60405290565b60405160a081016001600160401b0381118282101715612ba857612ba8612b70565b60405160c081016001600160401b0381118282101715612ba857612ba8612b70565b6040516101a081016001600160401b0381118282101715612ba857612ba8612b70565b604051606081016001600160401b0381118282101715612ba857612ba8612b70565b6040516102c081016001600160401b0381118282101715612ba857612ba8612b70565b604051601f8201601f191681016001600160401b0381118282101715612c8257612c82612b70565b604052919050565b60006001600160401b03821115612ca357612ca3612b70565b5060051b60200190565b600082601f830112612cbe57600080fd5b81356020612cd3612cce83612c8a565b612c5a565b8083825260208201915060208460051b870101935086841115612cf557600080fd5b602086015b84811015612d1857612d0b81612b59565b8352918301918301612cfa565b509695505050505050565b600082601f830112612d3457600080fd5b81356020612d44612cce83612c8a565b8083825260208201915060208460051b870101935086841115612d6657600080fd5b602086015b84811015612d1857803560ff81168114612d855760008081fd5b8352918301918301612d6b565b600080600080600060a08688031215612daa57600080fd5b85359450612dba60208701612b59565b935060408601356001600160401b0380821115612dd657600080fd5b612de289838a01612cad565b94506060880135915080821115612df857600080fd5b612e0489838a01612d23565b93506080880135915080821115612e1a57600080fd5b50612e2788828901612cad565b9150509295509295909350565b600082601f830112612e4557600080fd5b81356020612e55612cce83612c8a565b8083825260208201915060208460051b870101935086841115612e7757600080fd5b602086015b84811015612d185780358352918301918301612e7c565b600082601f830112612ea457600080fd5b81356020612eb4612cce83612c8a565b82815260059290921b84018101918181019086841115612ed357600080fd5b8286015b84811015612d185780356001600160401b03811115612ef65760008081fd5b612f048986838b0101612e34565b845250918301918301612ed7565b600082601f830112612f2357600080fd5b81356020612f33612cce83612c8a565b8083825260208201915060208460051b870101935086841115612f5557600080fd5b602086015b84811015612d185780358015158114612f735760008081fd5b8352918301918301612f5a565b600082601f830112612f9157600080fd5b81356020612fa1612cce83612c8a565b82815260059290921b84018101918181019086841115612fc057600080fd5b8286015b84811015612d185780356001600160401b03811115612fe35760008081fd5b612ff18986838b0101612f12565b845250918301918301612fc4565b600082601f83011261301057600080fd5b81356020613020612cce83612c8a565b8083825260208201915060208460051b87010193508684111561304257600080fd5b602086015b84811015612d185780356001600160401b03811681146130675760008081fd5b8352918301918301613047565b600082601f83011261308557600080fd5b81356020613095612cce83612c8a565b82815260059290921b840181019181810190868411156130b457600080fd5b8286015b84811015612d185780356001600160401b038111156130d75760008081fd5b6130e58986838b0101612fff565b8452509183019183016130b8565b600060e0828403121561310557600080fd5b61310d612b86565b905081356001600160401b038082111561312657600080fd5b61313285838601612cad565b8352602084013591508082111561314857600080fd5b61315485838601612d23565b6020840152604084013591508082111561316d57600080fd5b61317985838601612cad565b6040840152606084013591508082111561319257600080fd5b61319e85838601612e93565b606084015260808401359150808211156131b757600080fd5b6131c385838601612f80565b608084015260a08401359150808211156131dc57600080fd5b6131e885838601613074565b60a084015260c084013591508082111561320157600080fd5b5061320e84828501613074565b60c08301525092915050565b60008060006060848603121561322f57600080fd5b8335925061323f60208501612b59565b915060408401356001600160401b0381111561325a57600080fd5b613266868287016130f3565b9150509250925092565b600082601f83011261328157600080fd5b81356020613291612cce83612c8a565b828152600592831b85018201928282019190878511156132b057600080fd5b8387015b858110156133565780356001600160401b038111156132d35760008081fd5b8801603f81018a136132e55760008081fd5b8581013560406132f7612cce83612c8a565b82815291851b8301810191888101908d8411156133145760008081fd5b938201935b8385101561334557843592508260010b83146133355760008081fd5b8282529389019390890190613319565b8852505050938501935084016132b4565b5090979650505050505050565b600060a0828403121561337557600080fd5b61337d612bae565b905081356001600160401b038082111561339657600080fd5b6133a285838601612cad565b835260208401359150808211156133b857600080fd5b6133c485838601612d23565b602084015260408401359150808211156133dd57600080fd5b6133e985838601612cad565b6040840152606084013591508082111561340257600080fd5b61340e85838601612e93565b6060840152608084013591508082111561342757600080fd5b5061343484828501613270565b60808301525092915050565b6000806000806080858703121561345657600080fd5b8435935061346660208601612b59565b925060408501356001600160401b0381111561348157600080fd5b61348d87828801613363565b9250506060850135600681106134a257600080fd5b939692955090935050565b803563ffffffff81168114612b6b57600080fd5b600080600080608085870312156134d757600080fd5b843593506134e760208601612b59565b92506134f5604086016134ad565b9396929550929360600135925050565b60008060006060848603121561351a57600080fd5b8335925061352a60208501612b59565b9150604084013590509250925092565b600082601f83011261354b57600080fd5b8135602061355b612cce83612c8a565b828152600592831b850182019282820191908785111561357a57600080fd5b8387015b858110156133565780356001600160401b0381111561359d5760008081fd5b8801603f81018a136135af5760008081fd5b8581013560406135c1612cce83612c8a565b82815291851b8301810191888101908d8411156135de5760008081fd5b938201935b8385101561361557843592506001600160a01b03831683146136055760008081fd5b82825293890193908901906135e3565b88525050509385019350840161357e565b600082601f83011261363757600080fd5b81356020613647612cce83612c8a565b828152600592831b850182019282820191908785111561366657600080fd5b8387015b858110156133565780356001600160401b038111156136895760008081fd5b8801603f81018a1361369b5760008081fd5b8581013560406136ad612cce83612c8a565b82815291851b8301810191888101908d8411156136ca5760008081fd5b938201935b8385101561370157843592506001600160581b03831683146136f15760008081fd5b82825293890193908901906136cf565b88525050509385019350840161366a565b600060c0828403121561372457600080fd5b61372c612bd0565b905081356001600160401b038082111561374557600080fd5b61375185838601612cad565b8352602084013591508082111561376757600080fd5b61377385838601612d23565b6020840152604084013591508082111561378c57600080fd5b61379885838601612cad565b604084015260608401359150808211156137b157600080fd5b6137bd8583860161353a565b606084015260808401359150808211156137d657600080fd5b6137e285838601613626565b608084015260a08401359150808211156137fb57600080fd5b5061380884828501612f80565b60a08301525092915050565b60008060006060848603121561382957600080fd5b8335925061383960208501612b59565b915060408401356001600160401b0381111561385457600080fd5b61326686828701613712565b600060c0828403121561387257600080fd5b61387a612bd0565b905081356001600160401b038082111561389357600080fd5b61389f85838601612cad565b835260208401359150808211156138b557600080fd5b6138c185838601612d23565b602084015260408401359150808211156138da57600080fd5b6138e685838601612cad565b604084015260608401359150808211156138ff57600080fd5b61390b8583860161353a565b6060840152608084013591508082111561392457600080fd5b6137e285838601612f80565b60008060006060848603121561394557600080fd5b8335925061395560208501612b59565b915060408401356001600160401b0381111561397057600080fd5b61326686828701613860565b600082601f83011261398d57600080fd5b8135602061399d612cce83612c8a565b8083825260208201915060208460051b8701019350868411156139bf57600080fd5b602086015b84811015612d18576139d5816134ad565b83529183019183016139c4565b600060c082840312156139f457600080fd5b6139fc612bd0565b905081356001600160401b0380821115613a1557600080fd5b613a2185838601612cad565b83526020840135915080821115613a3757600080fd5b613a4385838601612d23565b60208401526040840135915080821115613a5c57600080fd5b613a6885838601612cad565b60408401526060840135915080821115613a8157600080fd5b613a8d8583860161397c565b60608401526080840135915080821115613aa657600080fd5b613ab28583860161397c565b608084015260a0840135915080821115613acb57600080fd5b5061380884828501612f12565b600080600060608486031215613aed57600080fd5b83359250613afd60208501612b59565b915060408401356001600160401b03811115613b1857600080fd5b613266868287016139e2565b600082601f830112613b3557600080fd5b81356020613b45612cce83612c8a565b82815260059290921b84018101918181019086841115613b6457600080fd5b8286015b84811015612d185780356001600160401b03811115613b875760008081fd5b613b958986838b0101612d23565b845250918301918301613b68565b600082601f830112613bb457600080fd5b81356020613bc4612cce83612c8a565b8083825260208201915060208460051b870101935086841115613be657600080fd5b602086015b84811015612d185780358060030b8114613c055760008081fd5b8352918301918301613beb565b600082601f830112613c2357600080fd5b81356020613c33612cce83612c8a565b82815260059290921b84018101918181019086841115613c5257600080fd5b8286015b84811015612d185780356001600160401b03811115613c755760008081fd5b613c838986838b0101613ba3565b845250918301918301613c56565b600082601f830112613ca257600080fd5b81356020613cb2612cce83612c8a565b82815260059290921b84018101918181019086841115613cd157600080fd5b8286015b84811015612d185780356001600160401b03811115613cf45760008081fd5b613d028986838b010161397c565b845250918301918301613cd5565b60006101a08284031215613d2357600080fd5b613d2b612bf2565b905081356001600160401b0380821115613d4457600080fd5b613d5085838601612cad565b83526020840135915080821115613d6657600080fd5b613d7285838601612d23565b60208401526040840135915080821115613d8b57600080fd5b613d9785838601612cad565b60408401526060840135915080821115613db057600080fd5b613dbc85838601613b24565b60608401526080840135915080821115613dd557600080fd5b613de185838601613c12565b608084015260a0840135915080821115613dfa57600080fd5b613e068583860161397c565b60a084015260c0840135915080821115613e1f57600080fd5b613e2b85838601613ba3565b60c084015260e0840135915080821115613e4457600080fd5b613e5085838601613ba3565b60e084015261010091508184013581811115613e6b57600080fd5b613e7786828701613ba3565b838501525061012091508184013581811115613e9257600080fd5b613e9e86828701613ba3565b838501525061014091508184013581811115613eb957600080fd5b613ec586828701613ba3565b838501525061016091508184013581811115613ee057600080fd5b613eec8682870161353a565b838501525061018091508184013581811115613f0757600080fd5b613f1386828701613c91565b8385015250505092915050565b600080600060608486031215613f3557600080fd5b83359250613f4560208501612b59565b915060408401356001600160401b03811115613f6057600080fd5b61326686828701613d10565b600082601f830112613f7d57600080fd5b81356020613f8d612cce83612c8a565b82815260059290921b84018101918181019086841115613fac57600080fd5b8286015b84811015612d185780356001600160401b03811115613fcf5760008081fd5b613fdd8986838b0101612cad565b845250918301918301613fb0565b600060a08284031215613ffd57600080fd5b614005612bae565b905081356001600160401b038082111561401e57600080fd5b61402a85838601612cad565b8352602084013591508082111561404057600080fd5b61404c85838601612d23565b6020840152604084013591508082111561406557600080fd5b61407185838601612cad565b6040840152606084013591508082111561408a57600080fd5b61409685838601612d23565b606084015260808401359150808211156140af57600080fd5b5061343484828501613f6c565b6000806000606084860312156140d157600080fd5b833592506140e160208501612b59565b915060408401356001600160401b038111156140fc57600080fd5b61326686828701613feb565b60006060828403121561411a57600080fd5b614122612c15565b905081356001600160401b038082111561413b57600080fd5b61414785838601612cad565b8352602084013591508082111561415d57600080fd5b61416985838601612d23565b6020840152604084013591508082111561418257600080fd5b5061418f84828501612cad565b60408301525092915050565b600060c082840312156141ad57600080fd5b6141b5612bd0565b905081356001600160401b03808211156141ce57600080fd5b6141da85838601612cad565b835260208401359150808211156141f057600080fd5b6141fc85838601612d23565b6020840152604084013591508082111561421557600080fd5b61422185838601612cad565b6040840152606084013591508082111561423a57600080fd5b61424685838601612f80565b6060840152608084013591508082111561425f57600080fd5b61426b85838601613b24565b608084015260a084013591508082111561428457600080fd5b5061380884828501613c12565b600060c082840312156142a357600080fd5b6142ab612bd0565b905081356001600160401b03808211156142c457600080fd5b6142d085838601612cad565b835260208401359150808211156142e657600080fd5b6142f285838601612d23565b6020840152604084013591508082111561430b57600080fd5b61431785838601612cad565b6040840152606084013591508082111561433057600080fd5b61433c85838601613b24565b6060840152608084013591508082111561435557600080fd5b6137e285838601613074565b60006060828403121561437357600080fd5b61437b612c15565b905081356001600160401b038082111561439457600080fd5b6143a085838601612cad565b835260208401359150808211156143b657600080fd5b6143c285838601612d23565b602084015260408401359150808211156143db57600080fd5b5061418f84828501613c91565b600080604083850312156143fb57600080fd5b8235915060208301356001600160401b038082111561441957600080fd5b908401906102c0828703121561442e57600080fd5b614436612c37565b61443f83612b59565b815260208301358281111561445357600080fd5b61445f88828601612e34565b60208301525060408301358281111561447757600080fd5b61448388828601612fff565b60408301525060608301358281111561449b57600080fd5b6144a788828601612fff565b6060830152506080830135828111156144bf57600080fd5b6144cb88828601612f12565b60808301525060a083013560a082015260c0830135828111156144ed57600080fd5b6144f988828601614108565b60c08301525060e08301358281111561451157600080fd5b61451d88828601613feb565b60e083015250610100808401358381111561453757600080fd5b6145438982870161419b565b828401525050610120808401358381111561455d57600080fd5b61456989828701613860565b828401525050610140808401358381111561458357600080fd5b61458f89828701613712565b82840152505061016080840135838111156145a957600080fd5b6145b5898287016139e2565b82840152505061018080840135838111156145cf57600080fd5b6145db898287016130f3565b8284015250506101a080840135838111156145f557600080fd5b614601898287016130f3565b8284015250506101c0808401358381111561461b57600080fd5b61462789828701614291565b8284015250506101e0808401358381111561464157600080fd5b61464d89828701614361565b828401525050610200808401358381111561466757600080fd5b61467389828701613d10565b828401525050610220808401358381111561468d57600080fd5b61469989828701613d10565b82840152505061024080840135838111156146b357600080fd5b6146bf89828701613363565b82840152505061026080840135838111156146d957600080fd5b6146e589828701613363565b82840152505061028080840135838111156146ff57600080fd5b61470b89828701613363565b8284015250506102a0808401358381111561472557600080fd5b61473189828701613363565b8284015250508093505050509250929050565b60008060006060848603121561475957600080fd5b8335925061476960208501612b59565b915060408401356001600160401b0381111561478457600080fd5b6132668682870161419b565b600080604083850312156147a357600080fd5b823591506147b3602084016134ad565b90509250929050565b60008060008060008060c087890312156147d557600080fd5b863595506147e560208801612b59565b945060408701356001600160401b0381111561480057600080fd5b61480c89828a01613d10565b945050606087013592506080870135915060a087013590509295509295509295565b60008060006060848603121561484357600080fd5b8335925061485360208501612b59565b915060408401356001600160401b0381111561486e57600080fd5b61326686828701614291565b600080600080600080600060e0888a03121561489557600080fd5b873596506148a560208901612b59565b955060408801356001600160401b03808211156148c157600080fd5b6148cd8b838c01612e34565b965060608a01359150808211156148e357600080fd5b6148ef8b838c01612fff565b955060808a013591508082111561490557600080fd5b6149118b838c01612fff565b945060a08a013591508082111561492757600080fd5b506149348a828b01612f12565b92505060c0880135905092959891949750929550565b60008060006060848603121561495f57600080fd5b8335925061496f60208501612b59565b915060408401356001600160401b0381111561498a57600080fd5b61326686828701614361565b634e487b7160e01b600052603260045260246000fd5b60008151808452602080850194506020840160005b838110156149e157815161ffff16875295820195908201906001016149c1565b509495945050505050565b60008151808452602080850194506020840160005b838110156149e157815160ff1687529582019590820190600101614a01565b61ffff85168152608060208201526000614a3d60808301866149ac565b8281036040840152614a4f81866149ec565b90508281036060840152614a6381856149ac565b979650505050505050565b600082825180855260208086019550808260051b8401018186016000805b85811015614adf57868403601f19018a52825180518086529086019086860190845b81811015614aca57835183529288019291880191600101614aae565b50509a86019a94505091840191600101614a8c565b509198975050505050505050565b60008151808452602080850194506020840160005b838110156149e1578151151587529582019590820190600101614b02565b60008282518085526020808601955060208260051b8401016020860160005b8481101561335657601f19868403018952614b5b838351614aed565b98840198925090830190600101614b3f565b600082825180855260208086019550808260051b8401018186016000805b85811015614adf57868403601f19018a52825180518086529086019086860190845b81811015614bd25783516001600160401b031683529288019291880191600101614bad565b50509a86019a94505091840191600101614b8b565b61ffff83168152604060208201526000825160e06040840152614c0e6101208401826149ac565b90506020840151603f1980858403016060860152614c2c83836149ec565b92506040860151915080858403016080860152614c4983836149ac565b925060608601519150808584030160a0860152614c668383614a6e565b925060808601519150808584030160c0860152614c838383614b20565b925060a08601519150808584030160e0860152614ca08383614b6d565b925060c08601519150808584030161010086015250614cbf8282614b6d565b9695505050505050565b634e487b7160e01b600052602160045260246000fd5b60068110614cfd57634e487b7160e01b600052602160045260246000fd5b9052565b61ffff8416815260006020606081840152845160a06060850152614d296101008501826149ac565b905081860151605f1980868403016080870152614d4683836149ec565b925060408801519150808684030160a0870152614d6383836149ac565b925060608801519150808684030160c0870152614d808383614a6e565b608089015187820390920160e08801528151808252909350908401915083830190600581901b840185016000805b83811015614e0457868303601f19018552855180518085529089019089850190845b81811015614def578351600190810b8452938c0193928c019201614dd0565b50509689019695890195935050600101614dae565b5050809650505050505050614e1c6040830184614cdf565b949350505050565b634e487b7160e01b600052601160045260246000fd5b600060ff821660ff8103614e5057614e50614e24565b60010192915050565b600082825180855260208086019550808260051b8401018186016000805b85811015614adf57868403601f19018a52825180518086529086019086860190845b81811015614ebe5783516001600160a01b031683529288019291880191600101614e99565b50509a86019a94505091840191600101614e77565b61ffff8316815260006020604081840152835160c06040850152614efb6101008501826149ac565b905081850151603f1980868403016060870152614f1883836149ec565b92506040870151915080868403016080870152614f3583836149ac565b925060608701519150808684030160a0870152614f528383614e59565b6080880151878203830160c089015280518083529194508501925084840190600581901b850186016000805b83811015614fda57878303601f1901855286518051808552908a01908a850190845b81811015614fc55783516001600160581b03168352928c0192918c0191600101614fa0565b5050978a0197958a0195935050600101614f7e565b505060a08a01519650838982030160e08a0152614ff78188614b20565b9b9a5050505050505050505050565b61ffff83168152604060208201526000825160c0604084015261502d6101008401826149ac565b90506020840151603f198085840301606086015261504b83836149ec565b9250604086015191508085840301608086015261506883836149ac565b925060608601519150808584030160a08601526150858383614e59565b925060808601519150808584030160c08601526150a28383614b20565b925060a08601519150808584030160e086015250614cbf8282614b20565b60008151808452602080850194506020840160005b838110156149e157815163ffffffff16875295820195908201906001016150d5565b61ffff83168152604060208201526000825160c0604084015261511e6101008401826149ac565b90506020840151603f198085840301606086015261513c83836149ec565b9250604086015191508085840301608086015261515983836149ac565b925060608601519150808584030160a086015261517683836150c0565b925060808601519150808584030160c086015261519383836150c0565b925060a08601519150808584030160e086015250614cbf8282614aed565b60008282518085526020808601955060208260051b8401016020860160005b8481101561335657601f198684030189526151ec8383516149ec565b988401989250908301906001016151d0565b60008151808452602080850194506020840160005b838110156149e157815160030b87529582019590820190600101615213565b60008282518085526020808601955060208260051b8401016020860160005b8481101561335657601f1986840301895261526d8383516151fe565b98840198925090830190600101615251565b60008282518085526020808601955060208260051b8401016020860160005b8481101561335657601f198684030189526152ba8383516150c0565b9884019892509083019060010161529e565b61ffff8316815260406020820152600082516101a08060408501526152f56101e08501836149ac565b91506020850151603f198086850301606087015261531384836149ec565b9350604087015191508086850301608087015261533084836149ac565b935060608701519150808685030160a087015261534d84836151b1565b935060808701519150808685030160c087015261536a8483615232565b935060a08701519150808685030160e087015261538784836150c0565b935060c087015191506101008187860301818801526153a685846151fe565b945060e088015192506101208288870301818901526153c586856151fe565b955081890151935061014091508288870301828901526153e586856151fe565b95508089015193505061016082888703018189015261540486856151fe565b9550818901519350610180915082888703018289015261542486856151fe565b9550808901519350508187860301848801526154408584614e59565b94508088015193505080868503016101c08701525050614cbf828261527f565b61ffff8316815260006020604081840152835160a0604085015261548760e08501826149ac565b905081850151603f19808684030160608701526154a483836149ec565b925060408701519150808684030160808701526154c183836149ac565b925060608701519150808684030160a08701526154de83836149ec565b608088015187820390920160c08801528151808252909350908401915083830190600581901b8401850160005b8281101561553957601f198683030184526155278286516149ac565b9487019493870193915060010161550b565b509998505050505050505050565b61ffff83168152604060208201526000825160c0604084015261556e6101008401826149ac565b90506020840151603f198085840301606086015261558c83836149ec565b925060408601519150808584030160808601526155a983836149ac565b925060608601519150808584030160a08601526155c68383614b20565b925060808601519150808584030160c08601526155e383836151b1565b925060a08601519150808584030160e086015250614cbf8282615232565b61ffff83168152604060208201526000825160c060408401526156286101008401826149ac565b90506020840151603f198085840301606086015261564683836149ec565b9250604086015191508085840301608086015261566383836149ac565b925060608601519150808584030160a086015261568083836151b1565b925060808601519150808584030160c08601526150a28383614b6d565b61ffff831681526040602082015260008251606060408401526156c360a08401826149ac565b90506020840151603f19808584030160608601526156e183836149ec565b9250604086015191508085840301608086015250614cbf828261527f565b60008261571c57634e487b7160e01b600052601260045260246000fd5b500490565b8082018082111561239457612394614e24565b808202811582820484141761239457612394614e24565b8181038181111561239457612394614e24565b634e487b7160e01b600052603160045260246000fdfea26469706673582212207929ebae6538389b539c782b6d5ea3a897e3ab30cf46ac02dcd4e1ef7d4486cb64736f6c63430008170033
Deployed Bytecode
0x73b8ba82f19a9be6cbf6daf9bf4fbcc5bdfcf8bee630146080604052600436106101165760003560e01c80639c9dc579116100a75780639c9dc5791461025d578063a84d660c1461027d578063a913674b1461029d578063b5083ab9146102bd578063b5287c91146102dd578063c258bf40146102fd578063c5828ff11461031d578063ca4739361461033d578063cbbefc281461035d578063d335b7f31461037d57600080fd5b806301c3192f1461011b57806301c7664c1461013d5780630324a18c1461015d5780631c9a90081461017d5780633e8dd8681461019d57806356814190146101bd578063679dde4c146101dd5780636b6cc9f8146101fd578063751dda431461021d5780638cd5642c1461023d575b600080fd5b81801561012757600080fd5b5061013b610136366004612d92565b61039d565b005b81801561014957600080fd5b5061013b61015836600461321a565b6104f9565b81801561016957600080fd5b5061013b610178366004613440565b610546565b81801561018957600080fd5b5061013b6101983660046134c1565b610757565b8180156101a957600080fd5b5061013b6101b8366004613505565b610808565b8180156101c957600080fd5b5061013b6101d8366004613814565b610be5565b8180156101e957600080fd5b5061013b6101f8366004613930565b610db4565b81801561020957600080fd5b5061013b610218366004613ad8565b610f47565b81801561022957600080fd5b5061013b610238366004613f20565b611048565b81801561024957600080fd5b5061013b6102583660046140bc565b611090565b81801561026957600080fd5b5061013b6102783660046143e8565b6111dd565b81801561028957600080fd5b5061013b610298366004614744565b61135d565b8180156102a957600080fd5b5061013b6102b8366004614790565b6114ea565b8180156102c957600080fd5b5061013b6102d8366004613f20565b611621565b8180156102e957600080fd5b5061013b6102f8366004613505565b611669565b81801561030957600080fd5b5061013b6103183660046147bc565b611880565b81801561032957600080fd5b5061013b61033836600461482e565b611c21565b81801561034957600080fd5b5061013b61035836600461487a565b611dae565b81801561036957600080fd5b5061013b61037836600461321a565b611fbb565b81801561038957600080fd5b5061013b61039836600461494a565b611ffb565b825160005b818110156104b3576103ce87878784815181106103c1576103c1614996565b60200260200101516120fe565b60008760060160006104218885815181106103eb576103eb614996565b602002602001015188868151811061040557610405614996565b60200260200101518b61ffff166121259092919063ffffffff16565b81526020019081526020016000209050600061048b898989868151811061044a5761044a614996565b602002602001015189878151811061046457610464614996565b602002602001015189888151811061047e5761047e614996565b6020026020010151612149565b905080156104a9578154600181018355600083815260209020018190555b50506001016103a2565b50507f49007bba987368aede38546653f41bd86c6ed04d5e7ba4ca166432c014b2138c848484846040516104ea9493929190614a20565b60405180910390a15050505050565b61050883838386600d0161218d565b7f49126b5a176699b1ec3b1eac8c744bfaf006d2cefcd10f794c25f29a3ace0b8e8282604051610539929190614be7565b60405180910390a1505050565b81515160005b81811015610714576000801b6105af87878760000151858151811061057357610573614996565b60200260200101518860200151868151811061059157610591614996565b60200260200101518960400151878151811061047e5761047e614996565b1461070c576000610633856000015183815181106105cf576105cf614996565b6020026020010151866020015184815181106105ed576105ed614996565b60200260200101518760400151858151811061060b5761060b614996565b602002602001015187600581111561062557610625614cc9565b61ffff8b1693929190612318565b600081815260158901602052604081209192505b8660600151848151811061065d5761065d614996565b60200260200101515181101561070857816106ed8860800151868151811061068757610687614996565b602002602001015183815181106106a0576106a0614996565b6020026020010151896060015187815181106106be576106be614996565b602002602001015184815181106106d7576106d7614996565b602002602001015161235a90919063ffffffff16565b81546001818101845560009384526020909320015501610647565b5050505b60010161054c565b50507f5144a9cbb149e306e7330f55e21a6b72086d1b86b4aef7a9d76713048802295183838360405161074993929190614d01565b60405180910390a150505050565b63ffffffff8216600081815260038601602090815260408083208054600160ff19918216811790925561ffff891680865260028b01855283862080549092168317909155858552898452828520805461ffff1916821790558085529089018352818420805463ffffffff191686179055601a89018352928190208590558051938452908301919091527f77b037a0afbc269c250968ad8197fbc330f78621131eb33de9de913bc8a510d69101610749565b6108106128c6565b61ffff831660608201819052600090815260058501602052604090206108358161238a565b60c0830181905283101561084b5760c082018390525b8160c001516001600160401b0381111561086757610867612b70565b604051908082528060200260200182016040528015610890578160200160208202803683370190505b5061012083015260005b8260c00151811015610b91576108b0828261239a565b61010084018190526101208401518051839081106108d0576108d0614996565b6020026020010181815250506108fa83610100015190601082901c90602083901c90602884901c90565b61ffff90811660a088015260ff909116865216608085015250610100830180516000908152600988016020908152604080832083905592518252600a890190529081206109469161291c565b6101008301516000908152600b8701602052604081206109659161291c565b6101008301516000908152600c8701602052604081206109849161291c565b6101008301516000908152600d8701602052604081206109a39161293d565b6101008301516000908152600e8701602052604081206109c29161293d565b6101008301516000908152600f8701602052604081206109e19161291c565b610100830180516000908152601088016020908152604080832083905592518252601189019052908120610a149161291c565b61010083015160009081526012870160205260408120610a339161291c565b610100830180516000908152601388016020908152604080832083905592518252601489019052908120610a669161291c565b61010083015160009081526016870160205260408120610a859161291c565b600060208401525b6007836020015160ff161015610b0757856007016000610ad4856080015186600001518760a001518860200151896060015161ffff1661231890949392919063ffffffff16565b81526020019081526020016000206000610aee919061295e565b826020018051610afd90614e3a565b60ff169052610a8d565b600160408401525b6005836040015160ff161015610b8957856015016000610b56856080015186600001518760a001518860400151896060015161ffff1661231890949392919063ffffffff16565b81526020019081526020016000206000610b70919061291c565b826040018051610b7f90614e3a565b60ff169052610b0f565b60010161089a565b5060005b82610120015151811015610bdd57610bd48361012001518281518110610bbd57610bbd614996565b6020026020010151836123ad90919063ffffffff16565b50600101610b95565b505050505050565b80515160005b81811015610d81576000610c4c868686600001518581518110610c1057610c10614996565b602002602001015187602001518681518110610c2e57610c2e614996565b60200260200101518860400151878151811061047e5761047e614996565b90508015610d78576000818152600c870160205260408120905b85606001518481518110610c7c57610c7c614996565b602002602001015151811015610d75576000610d4e87608001518681518110610ca757610ca7614996565b60200260200101518381518110610cc057610cc0614996565b60200260200101518860a001518781518110610cde57610cde614996565b60200260200101518481518110610cf757610cf7614996565b602002602001015189606001518881518110610d1557610d15614996565b60200260200101518581518110610d2e57610d2e614996565b60200260200101516001600160a01b03166123b99092919063ffffffff16565b90508015610d6c578254600181018455600084815260209020018190555b50600101610c66565b50505b50600101610beb565b50507ff7763619246d8f0933bc30f26ed64aa852419d4c03682ebe62c07b2b5e58189a8282604051610539929190614ed3565b80515160005b81811015610f14576000610ddf868686600001518581518110610c1057610c10614996565b90508015610f0b576000818152600b870160205260408120905b85606001518481518110610e0f57610e0f614996565b602002602001015151811015610f08576000610ee187608001518681518110610e3a57610e3a614996565b60200260200101518381518110610e5357610e53614996565b60200260200101518860a001518781518110610e7157610e71614996565b60200260200101518481518110610e8a57610e8a614996565b602002602001015189606001518881518110610ea857610ea8614996565b60200260200101518581518110610ec157610ec1614996565b60200260200101516001600160a01b03166123f39092919063ffffffff16565b90508015610eff578254600181018455600084815260209020018190555b50600101610df9565b50505b50600101610dba565b50507f75e2a553fd5c4396591939214dccd44f121aa91d9419cb35347afa55e0335bab8282604051610539929190615006565b80515160005b81811015611015576000610f72868686600001518581518110610c1057610c10614996565b9050801561100c576000610fee85608001518481518110610f9557610f95614996565b60200260200101518660a001518581518110610fb357610fb3614996565b602002602001015187606001518681518110610fd157610fd1614996565b602002602001015163ffffffff166124229092919063ffffffff16565b9050801561100a57600082815260098801602052604090208190555b505b50600101610f4d565b50507fee4f8e4fe9011c317080a888f3ea5fd4984b1f407ba1eeb36840cdb64ceef48982826040516105399291906150f7565b61105f83838386600f018760100188601101611880565b7f869c57977aa939d74e2da756cb2e738c080b4231a44f4cd81950529f2ff6bbdd82826040516105399291906152cc565b80515160005b818110156111aa5760006110bb868686600001518581518110610c1057610c10614996565b905080156111a1576000611156856000015184815181106110de576110de614996565b6020026020010151866020015185815181106110fc576110fc614996565b60200260200101518760400151868151811061111a5761111a614996565b60200260200101518860600151878151811061113857611138614996565b60200260200101518a61ffff1661231890949392919063ffffffff16565b90508460800151838151811061116e5761116e614996565b6020026020010151876007016000838152602001908152602001600020908051906020019061119e929190612983565b50505b50600101611096565b50507f978987b8c70f4ee959e5636ae6f9699d1e1473928283af297505ae4147f96ed88282604051610539929190615460565b6111f1828260000151836101c00151611c21565b611205828260000151836101e00151611ffb565b805160c08201518051602082015160409092015161122793869390929161039d565b61123a8282600001518360e00151611090565b61124e82826000015183610100015161135d565b611262828260000151836101200151610db4565b611276828260000151836101400151610be5565b61128a828260000151836101600151610f47565b61129e8282600001518361018001516104f9565b6112b2828260000151836101a00151611fbb565b6112c6828260000151836102000151611048565b6112da828260000151836102200151611621565b6112f08282600001518361024001516001610546565b6113068282600001518361026001516002610546565b61131c8282600001518361028001516003610546565b611332828260000151836102a001516004610546565b61135982826000015183602001518460400151856060015186608001518760a00151611dae565b5050565b80515160005b818110156114b7576000611388868686600001518581518110610c1057610c10614996565b905080156114ae576000818152600a870160205260408120905b856060015184815181106113b8576113b8614996565b6020026020010151518110156114ab5760006114848760a0015186815181106113e3576113e3614996565b602002602001015183815181106113fc576113fc614996565b60200260200101518860600151878151811061141a5761141a614996565b6020026020010151848151811061143357611433614996565b60200260200101518960800151888151811061145157611451614996565b6020026020010151858151811061146a5761146a614996565b602002602001015160ff166124469092919063ffffffff16565b905080156114a2578254600181018455600084815260209020018190555b506001016113a2565b50505b50600101611363565b50507f6ae02f4d7862466cf651e781506106bd39987973bcbd68688021b5bcfbd8f6848282604051610539929190615547565b63ffffffff811660009081526020839052604090205461ffff161580611528575063ffffffff8116600090815260038301602052604090205460ff16155b1561154657604051634f12d70760e11b815260040160405180910390fd5b63ffffffff8116600081815260208481526040808320805461ffff16808552600288018452828520805460ff19908116909155825461ffff1916909255600188018452828520805463ffffffff1916905594845260038701835281842080549091169055838352601a860182528083208390556017860190915281206115cb9161293d565b61ffff81166000818152601885016020908152604080832092909255815163ffffffff86168152908101929092527f36206a58dc94b3b1d63eaf20aff0d8b78da875c59739496e88c021ad18422a129101610539565b611638838383866012018760130188601401611880565b7f4b868e06a41dbd00f1781690c7c901b28487927ffb0dd540e9c643fda33232bb82826040516105399291906152cc565b6116716128c6565b61ffff831660608201819052600090815260048501602052604090206116968161238a565b60c083018190528310156116ac5760c082018390525b8160c001516001600160401b038111156116c8576116c8612b70565b6040519080825280602002602001820160405280156116f1578160200160208202803683370190505b5060e083015260005b8260c0015181101561180757611710828261239a565b8360e00151828151811061172657611726614996565b6020026020010181815250508260e00151818151811061174857611748614996565b602090810291909101015161ffff166080840152600083525b6007835160ff1610156117ff5760808301518351606085015160068901926000926117919261ffff169190612125565b815260200190815260200160002060006117ab919061291c565b60808301518351606085015160088901926000926117ce9261ffff169190612125565b815260200190815260200160002060006117e89190612a2c565b825183906117f590614e3a565b60ff169052611761565b6001016116fa565b5060005b8260e0015151811015610bdd576118318360e001518281518110610bbd57610bbd614996565b611878578260e00151818151811061184b5761184b614996565b6020026020010151604051632fd1568f60e11b815260040161186f91815260200190565b60405180910390fd5b60010161180b565b83515160005b81811015611c175760006118e78989896000015185815181106118ab576118ab614996565b60200260200101518a6020015186815181106118c9576118c9614996565b60200260200101518b60400151878151811061047e5761047e614996565b90508015611c0e578660600151828151811061190557611905614996565b602002602001015151600014611985576119628760600151838151811061192e5761192e614996565b60200260200101518860800151848151811061194c5761194c614996565b602002602001015161246590919063ffffffff16565b60008281526020888152604090912082516119839391929190910190612a51565b505b6000611aa48860a0015184815181106119a0576119a0614996565b60200260200101518960c0015185815181106119be576119be614996565b60200260200101518a60e0015186815181106119dc576119dc614996565b60200260200101518b610100015187815181106119fb576119fb614996565b60200260200101518c61012001518881518110611a1a57611a1a614996565b60200260200101518d61014001518981518110611a3957611a39614996565b60209081029190910181015163ffffffff9690961694901b63ffffffff60201b169390931760409290921b63ffffffff60401b169190911760609190911b63ffffffff60601b161760809190911b63ffffffff60801b161760a09190911b63ffffffff60a01b161790565b90508015611abe5760008281526020879052604090208190555b60008861016001518481518110611ad757611ad7614996565b602002602001015151905080600014611c0b576000816001600160401b03811115611b0457611b04612b70565b604051908082528060200260200182016040528015611b2d578160200160208202803683370190505b50905060005b82811015611bea57611bc58b61018001518781518110611b5557611b55614996565b60200260200101518281518110611b6e57611b6e614996565b60200260200101518c61016001518881518110611b8d57611b8d614996565b60200260200101518381518110611ba657611ba6614996565b60200260200101516001600160a01b03166126c390919063ffffffff16565b828281518110611bd757611bd7614996565b6020908102919091010152600101611b33565b506000848152602088815260409091208251611c0892840190612a51565b50505b50505b50600101611886565b5050505050505050565b80515160005b81811015611d7b576000611c4c868686600001518581518110610c1057610c10614996565b90508015611d725760008181526016870160205260408120905b85606001518481518110611c7c57611c7c614996565b602002602001015151811015611d6f576000611d4887608001518681518110611ca757611ca7614996565b60200260200101518381518110611cc057611cc0614996565b60200260200101518860a001518781518110611cde57611cde614996565b60200260200101518481518110611cf757611cf7614996565b602002602001015189606001518881518110611d1557611d15614996565b60200260200101518581518110611d2e57611d2e614996565b602002602001015160ff166126e19092919063ffffffff16565b90508015611d66578254600181018455600084815260209020018190555b50600101611c66565b50505b50600101611c27565b50507fe8aef7992dddcf8eab441423fa358ec12b14f62644161a4641efd65f5e5a8f0d8282604051610539929190615601565b61ffff86166000818152601889016020908152604091829020849055815192835282018390527f979e00d9ab95347fc5a2a8936556e51fbf30d23aa2b43e95a307f37590db590e910160405180910390a161ffff861660009081526017880160205260408120905b8651811015611fb057816040518060400160405280898481518110611e3d57611e3d614996565b60200260200101518152602001611eb3888581518110611e5f57611e5f614996565b6020026020010151888681518110611e7957611e79614996565b60200260200101518b8781518110611e9357611e93614996565b60200260200101516001600160401b03166127049092919063ffffffff16565b905281546001818101845560009384526020938490208351600290930201918255929091015191015586517fa4f88bdc8eedabd5953d882c5d392f683d96cbda2c76003a7f0340083bc6148b908990899084908110611f1457611f14614996565b6020026020010151888481518110611f2e57611f2e614996565b6020026020010151888581518110611f4857611f48614996565b6020026020010151888681518110611f6257611f62614996565b6020908102919091018101516040805161ffff9097168752918601949094526001600160401b03928316908501521660608301521515608082015260a00160405180910390a1600101611e16565b505050505050505050565b611fca83838386600e0161218d565b7f0d0f8f5e403cc74ac4909b11cbfe60cc6e48a9973ba32e10b9449a7365c34e688282604051610539929190614be7565b80515160005b818110156120cb576120248585856000015184815181106103c1576103c1614996565b60006120798460000151838151811061203f5761203f614996565b60200260200101518560200151848151811061205d5761205d614996565b60200260200101518761ffff166121259092919063ffffffff16565b90508360400151828151811061209157612091614996565b602002602001015186600801600083815260200190815260200160002090805190602001906120c1929190612a8c565b5050600101612001565b50507f3068977f27d58675414225648f9fddefc01a84758bcc359b64f244480b30f770828260405161053992919061569d565b61ffff8281166000908152600485016020526040902061211f91831661272f565b50505050565b61ffff9290921660109190911b63ffff0000161760209190911b60ff60201b161790565b61ffff8416601084901b63ffff00001617602083901b60ff60201b1617602882901b61ffff60281b161780156121845761218486868361273b565b95945050505050565b81515160005b81811015610bdd5760006121b887878760000151858151811061057357610573614996565b9050801561230f57600081815260208590526040812060608701518051919291859081106121e8576121e8614996565b6020026020010151905060008760800151858151811061220a5761220a614996565b6020026020010151905060008860a00151868151811061222c5761222c614996565b6020026020010151905060008960c00151878151811061224e5761224e614996565b6020026020010151905060005b84518110156123085785604051806040016040528087848151811061228257612282614996565b602002602001015181526020016122d88585815181106122a4576122a4614996565b60200260200101518886815181106122be576122be614996565b6020026020010151888781518110611e9357611e93614996565b9052815460018181018455600093845260209384902083516002909302019182559290910151908201550161225b565b5050505050505b50600101612193565b61ffff9490941660109390931b63ffff0000169290921760209190911b60ff60201b161760289190911b61ffff60281b161760389190911b60ff60381b161790565b600061ffff19831683146123815760405163508af79560e01b815260040160405180910390fd5b5061ffff161790565b6000612394825490565b92915050565b60006123a6838361275a565b9392505050565b60006123a68383612784565b600160a01b600160f81b0360a083901b166001600160a01b0384161760f8826123e35760006123e6565b60015b60ff16901b179392505050565b6001600160a01b03831660a08361240b57600061240e565b60015b60ff16901b1760a8826123e35760006123e6565b63ffffffff60201b602083901b1663ffffffff8416176040826123e35760006123e6565b64ffffffff00600883901b1660ff8416176028826123e35760006123e6565b606081518351146124895760405163586cb9e160e01b815260040160405180910390fd5b60006008845161249991906156ff565b6124a4906001615721565b90506000816001600160401b038111156124c0576124c0612b70565b6040519080825280602002602001820160405280156124e9578160200160208202803683370190505b50905060005b828110156126ba5760005b60088110156126b157600081612511846008615734565b61251b9190615721565b90508751811061252b57506126b1565b627fffff60020b88828151811061254457612544614996565b602002602001015160030b138061257d5750627fffff1960020b88828151811061257057612570614996565b602002602001015160030b125b156125bb5787818151811061259457612594614996565b602002602001015160030b604051632eb1d06960e01b815260040161186f91815260200190565b8781815181106125cd576125cd614996565b602002602001015160030b6000036125f857604051637c946ed760e01b815260040160405180910390fd5b612603826020615734565b88828151811061261557612615614996565b602002602001015162ffffff1660001b901b84848151811061263957612639614996565b6020026020010181815117915081815250508160206126589190615734565b612663906018615721565b87828151811061267557612675614996565b602002602001015160ff1660001b901b84848151811061269757612697614996565b6020908102919091010180519091179052506001016124fa565b506001016124ef565b50949350505050565b63ffffffff60a01b60a09190911b166001600160a01b039091161790565b68ffffffffffffffff00600883901b1660ff8416176048826123e35760006123e6565b67ffffffffffffffff60401b604083901b166001600160401b038416176080826123e35760006123e6565b60006123a68383612877565b61ffff82166000908152600584016020526040902061211f908261272f565b600082600001828154811061277157612771614996565b9060005260206000200154905092915050565b6000818152600183016020526040812054801561286d5760006127a860018361574b565b85549091506000906127bc9060019061574b565b90508082146128215760008660000182815481106127dc576127dc614996565b90600052602060002001549050808760000184815481106127ff576127ff614996565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806128325761283261575e565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050612394565b6000915050612394565b60008181526001830160205260408120546128be57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155612394565b506000612394565b604080516101408101825260008082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820183905260e0820181905261010082019290925261012081019190915290565b508054600082559060005260206000209081019061293a9190612b29565b50565b508054600082556002029060005260206000209081019061293a9190612b3e565b50805460008255600f01601090049060005260206000209081019061293a9190612b29565b82805482825590600052602060002090600f01601090048101928215612a1c5791602002820160005b838211156129ec57835183826101000a81548161ffff021916908361ffff16021790555092602001926002016020816001010492830192600103026129ac565b8015612a1a5782816101000a81549061ffff02191690556002016020816001010492830192600103026129ec565b505b50612a28929150612b29565b5090565b50805460008255600701600890049060005260206000209081019061293a9190612b29565b828054828255906000526020600020908101928215612a1c579160200282015b82811115612a1c578251825591602001919060010190612a71565b82805482825590600052602060002090600701600890048101928215612a1c5791602002820160005b83821115612af957835183826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302612ab5565b8015612a1a5782816101000a81549063ffffffff0219169055600401602081600301049283019260010302612af9565b5b80821115612a285760008155600101612b2a565b5b80821115612a285760008082556001820155600201612b3f565b803561ffff81168114612b6b57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60405160e081016001600160401b0381118282101715612ba857612ba8612b70565b60405290565b60405160a081016001600160401b0381118282101715612ba857612ba8612b70565b60405160c081016001600160401b0381118282101715612ba857612ba8612b70565b6040516101a081016001600160401b0381118282101715612ba857612ba8612b70565b604051606081016001600160401b0381118282101715612ba857612ba8612b70565b6040516102c081016001600160401b0381118282101715612ba857612ba8612b70565b604051601f8201601f191681016001600160401b0381118282101715612c8257612c82612b70565b604052919050565b60006001600160401b03821115612ca357612ca3612b70565b5060051b60200190565b600082601f830112612cbe57600080fd5b81356020612cd3612cce83612c8a565b612c5a565b8083825260208201915060208460051b870101935086841115612cf557600080fd5b602086015b84811015612d1857612d0b81612b59565b8352918301918301612cfa565b509695505050505050565b600082601f830112612d3457600080fd5b81356020612d44612cce83612c8a565b8083825260208201915060208460051b870101935086841115612d6657600080fd5b602086015b84811015612d1857803560ff81168114612d855760008081fd5b8352918301918301612d6b565b600080600080600060a08688031215612daa57600080fd5b85359450612dba60208701612b59565b935060408601356001600160401b0380821115612dd657600080fd5b612de289838a01612cad565b94506060880135915080821115612df857600080fd5b612e0489838a01612d23565b93506080880135915080821115612e1a57600080fd5b50612e2788828901612cad565b9150509295509295909350565b600082601f830112612e4557600080fd5b81356020612e55612cce83612c8a565b8083825260208201915060208460051b870101935086841115612e7757600080fd5b602086015b84811015612d185780358352918301918301612e7c565b600082601f830112612ea457600080fd5b81356020612eb4612cce83612c8a565b82815260059290921b84018101918181019086841115612ed357600080fd5b8286015b84811015612d185780356001600160401b03811115612ef65760008081fd5b612f048986838b0101612e34565b845250918301918301612ed7565b600082601f830112612f2357600080fd5b81356020612f33612cce83612c8a565b8083825260208201915060208460051b870101935086841115612f5557600080fd5b602086015b84811015612d185780358015158114612f735760008081fd5b8352918301918301612f5a565b600082601f830112612f9157600080fd5b81356020612fa1612cce83612c8a565b82815260059290921b84018101918181019086841115612fc057600080fd5b8286015b84811015612d185780356001600160401b03811115612fe35760008081fd5b612ff18986838b0101612f12565b845250918301918301612fc4565b600082601f83011261301057600080fd5b81356020613020612cce83612c8a565b8083825260208201915060208460051b87010193508684111561304257600080fd5b602086015b84811015612d185780356001600160401b03811681146130675760008081fd5b8352918301918301613047565b600082601f83011261308557600080fd5b81356020613095612cce83612c8a565b82815260059290921b840181019181810190868411156130b457600080fd5b8286015b84811015612d185780356001600160401b038111156130d75760008081fd5b6130e58986838b0101612fff565b8452509183019183016130b8565b600060e0828403121561310557600080fd5b61310d612b86565b905081356001600160401b038082111561312657600080fd5b61313285838601612cad565b8352602084013591508082111561314857600080fd5b61315485838601612d23565b6020840152604084013591508082111561316d57600080fd5b61317985838601612cad565b6040840152606084013591508082111561319257600080fd5b61319e85838601612e93565b606084015260808401359150808211156131b757600080fd5b6131c385838601612f80565b608084015260a08401359150808211156131dc57600080fd5b6131e885838601613074565b60a084015260c084013591508082111561320157600080fd5b5061320e84828501613074565b60c08301525092915050565b60008060006060848603121561322f57600080fd5b8335925061323f60208501612b59565b915060408401356001600160401b0381111561325a57600080fd5b613266868287016130f3565b9150509250925092565b600082601f83011261328157600080fd5b81356020613291612cce83612c8a565b828152600592831b85018201928282019190878511156132b057600080fd5b8387015b858110156133565780356001600160401b038111156132d35760008081fd5b8801603f81018a136132e55760008081fd5b8581013560406132f7612cce83612c8a565b82815291851b8301810191888101908d8411156133145760008081fd5b938201935b8385101561334557843592508260010b83146133355760008081fd5b8282529389019390890190613319565b8852505050938501935084016132b4565b5090979650505050505050565b600060a0828403121561337557600080fd5b61337d612bae565b905081356001600160401b038082111561339657600080fd5b6133a285838601612cad565b835260208401359150808211156133b857600080fd5b6133c485838601612d23565b602084015260408401359150808211156133dd57600080fd5b6133e985838601612cad565b6040840152606084013591508082111561340257600080fd5b61340e85838601612e93565b6060840152608084013591508082111561342757600080fd5b5061343484828501613270565b60808301525092915050565b6000806000806080858703121561345657600080fd5b8435935061346660208601612b59565b925060408501356001600160401b0381111561348157600080fd5b61348d87828801613363565b9250506060850135600681106134a257600080fd5b939692955090935050565b803563ffffffff81168114612b6b57600080fd5b600080600080608085870312156134d757600080fd5b843593506134e760208601612b59565b92506134f5604086016134ad565b9396929550929360600135925050565b60008060006060848603121561351a57600080fd5b8335925061352a60208501612b59565b9150604084013590509250925092565b600082601f83011261354b57600080fd5b8135602061355b612cce83612c8a565b828152600592831b850182019282820191908785111561357a57600080fd5b8387015b858110156133565780356001600160401b0381111561359d5760008081fd5b8801603f81018a136135af5760008081fd5b8581013560406135c1612cce83612c8a565b82815291851b8301810191888101908d8411156135de5760008081fd5b938201935b8385101561361557843592506001600160a01b03831683146136055760008081fd5b82825293890193908901906135e3565b88525050509385019350840161357e565b600082601f83011261363757600080fd5b81356020613647612cce83612c8a565b828152600592831b850182019282820191908785111561366657600080fd5b8387015b858110156133565780356001600160401b038111156136895760008081fd5b8801603f81018a1361369b5760008081fd5b8581013560406136ad612cce83612c8a565b82815291851b8301810191888101908d8411156136ca5760008081fd5b938201935b8385101561370157843592506001600160581b03831683146136f15760008081fd5b82825293890193908901906136cf565b88525050509385019350840161366a565b600060c0828403121561372457600080fd5b61372c612bd0565b905081356001600160401b038082111561374557600080fd5b61375185838601612cad565b8352602084013591508082111561376757600080fd5b61377385838601612d23565b6020840152604084013591508082111561378c57600080fd5b61379885838601612cad565b604084015260608401359150808211156137b157600080fd5b6137bd8583860161353a565b606084015260808401359150808211156137d657600080fd5b6137e285838601613626565b608084015260a08401359150808211156137fb57600080fd5b5061380884828501612f80565b60a08301525092915050565b60008060006060848603121561382957600080fd5b8335925061383960208501612b59565b915060408401356001600160401b0381111561385457600080fd5b61326686828701613712565b600060c0828403121561387257600080fd5b61387a612bd0565b905081356001600160401b038082111561389357600080fd5b61389f85838601612cad565b835260208401359150808211156138b557600080fd5b6138c185838601612d23565b602084015260408401359150808211156138da57600080fd5b6138e685838601612cad565b604084015260608401359150808211156138ff57600080fd5b61390b8583860161353a565b6060840152608084013591508082111561392457600080fd5b6137e285838601612f80565b60008060006060848603121561394557600080fd5b8335925061395560208501612b59565b915060408401356001600160401b0381111561397057600080fd5b61326686828701613860565b600082601f83011261398d57600080fd5b8135602061399d612cce83612c8a565b8083825260208201915060208460051b8701019350868411156139bf57600080fd5b602086015b84811015612d18576139d5816134ad565b83529183019183016139c4565b600060c082840312156139f457600080fd5b6139fc612bd0565b905081356001600160401b0380821115613a1557600080fd5b613a2185838601612cad565b83526020840135915080821115613a3757600080fd5b613a4385838601612d23565b60208401526040840135915080821115613a5c57600080fd5b613a6885838601612cad565b60408401526060840135915080821115613a8157600080fd5b613a8d8583860161397c565b60608401526080840135915080821115613aa657600080fd5b613ab28583860161397c565b608084015260a0840135915080821115613acb57600080fd5b5061380884828501612f12565b600080600060608486031215613aed57600080fd5b83359250613afd60208501612b59565b915060408401356001600160401b03811115613b1857600080fd5b613266868287016139e2565b600082601f830112613b3557600080fd5b81356020613b45612cce83612c8a565b82815260059290921b84018101918181019086841115613b6457600080fd5b8286015b84811015612d185780356001600160401b03811115613b875760008081fd5b613b958986838b0101612d23565b845250918301918301613b68565b600082601f830112613bb457600080fd5b81356020613bc4612cce83612c8a565b8083825260208201915060208460051b870101935086841115613be657600080fd5b602086015b84811015612d185780358060030b8114613c055760008081fd5b8352918301918301613beb565b600082601f830112613c2357600080fd5b81356020613c33612cce83612c8a565b82815260059290921b84018101918181019086841115613c5257600080fd5b8286015b84811015612d185780356001600160401b03811115613c755760008081fd5b613c838986838b0101613ba3565b845250918301918301613c56565b600082601f830112613ca257600080fd5b81356020613cb2612cce83612c8a565b82815260059290921b84018101918181019086841115613cd157600080fd5b8286015b84811015612d185780356001600160401b03811115613cf45760008081fd5b613d028986838b010161397c565b845250918301918301613cd5565b60006101a08284031215613d2357600080fd5b613d2b612bf2565b905081356001600160401b0380821115613d4457600080fd5b613d5085838601612cad565b83526020840135915080821115613d6657600080fd5b613d7285838601612d23565b60208401526040840135915080821115613d8b57600080fd5b613d9785838601612cad565b60408401526060840135915080821115613db057600080fd5b613dbc85838601613b24565b60608401526080840135915080821115613dd557600080fd5b613de185838601613c12565b608084015260a0840135915080821115613dfa57600080fd5b613e068583860161397c565b60a084015260c0840135915080821115613e1f57600080fd5b613e2b85838601613ba3565b60c084015260e0840135915080821115613e4457600080fd5b613e5085838601613ba3565b60e084015261010091508184013581811115613e6b57600080fd5b613e7786828701613ba3565b838501525061012091508184013581811115613e9257600080fd5b613e9e86828701613ba3565b838501525061014091508184013581811115613eb957600080fd5b613ec586828701613ba3565b838501525061016091508184013581811115613ee057600080fd5b613eec8682870161353a565b838501525061018091508184013581811115613f0757600080fd5b613f1386828701613c91565b8385015250505092915050565b600080600060608486031215613f3557600080fd5b83359250613f4560208501612b59565b915060408401356001600160401b03811115613f6057600080fd5b61326686828701613d10565b600082601f830112613f7d57600080fd5b81356020613f8d612cce83612c8a565b82815260059290921b84018101918181019086841115613fac57600080fd5b8286015b84811015612d185780356001600160401b03811115613fcf5760008081fd5b613fdd8986838b0101612cad565b845250918301918301613fb0565b600060a08284031215613ffd57600080fd5b614005612bae565b905081356001600160401b038082111561401e57600080fd5b61402a85838601612cad565b8352602084013591508082111561404057600080fd5b61404c85838601612d23565b6020840152604084013591508082111561406557600080fd5b61407185838601612cad565b6040840152606084013591508082111561408a57600080fd5b61409685838601612d23565b606084015260808401359150808211156140af57600080fd5b5061343484828501613f6c565b6000806000606084860312156140d157600080fd5b833592506140e160208501612b59565b915060408401356001600160401b038111156140fc57600080fd5b61326686828701613feb565b60006060828403121561411a57600080fd5b614122612c15565b905081356001600160401b038082111561413b57600080fd5b61414785838601612cad565b8352602084013591508082111561415d57600080fd5b61416985838601612d23565b6020840152604084013591508082111561418257600080fd5b5061418f84828501612cad565b60408301525092915050565b600060c082840312156141ad57600080fd5b6141b5612bd0565b905081356001600160401b03808211156141ce57600080fd5b6141da85838601612cad565b835260208401359150808211156141f057600080fd5b6141fc85838601612d23565b6020840152604084013591508082111561421557600080fd5b61422185838601612cad565b6040840152606084013591508082111561423a57600080fd5b61424685838601612f80565b6060840152608084013591508082111561425f57600080fd5b61426b85838601613b24565b608084015260a084013591508082111561428457600080fd5b5061380884828501613c12565b600060c082840312156142a357600080fd5b6142ab612bd0565b905081356001600160401b03808211156142c457600080fd5b6142d085838601612cad565b835260208401359150808211156142e657600080fd5b6142f285838601612d23565b6020840152604084013591508082111561430b57600080fd5b61431785838601612cad565b6040840152606084013591508082111561433057600080fd5b61433c85838601613b24565b6060840152608084013591508082111561435557600080fd5b6137e285838601613074565b60006060828403121561437357600080fd5b61437b612c15565b905081356001600160401b038082111561439457600080fd5b6143a085838601612cad565b835260208401359150808211156143b657600080fd5b6143c285838601612d23565b602084015260408401359150808211156143db57600080fd5b5061418f84828501613c91565b600080604083850312156143fb57600080fd5b8235915060208301356001600160401b038082111561441957600080fd5b908401906102c0828703121561442e57600080fd5b614436612c37565b61443f83612b59565b815260208301358281111561445357600080fd5b61445f88828601612e34565b60208301525060408301358281111561447757600080fd5b61448388828601612fff565b60408301525060608301358281111561449b57600080fd5b6144a788828601612fff565b6060830152506080830135828111156144bf57600080fd5b6144cb88828601612f12565b60808301525060a083013560a082015260c0830135828111156144ed57600080fd5b6144f988828601614108565b60c08301525060e08301358281111561451157600080fd5b61451d88828601613feb565b60e083015250610100808401358381111561453757600080fd5b6145438982870161419b565b828401525050610120808401358381111561455d57600080fd5b61456989828701613860565b828401525050610140808401358381111561458357600080fd5b61458f89828701613712565b82840152505061016080840135838111156145a957600080fd5b6145b5898287016139e2565b82840152505061018080840135838111156145cf57600080fd5b6145db898287016130f3565b8284015250506101a080840135838111156145f557600080fd5b614601898287016130f3565b8284015250506101c0808401358381111561461b57600080fd5b61462789828701614291565b8284015250506101e0808401358381111561464157600080fd5b61464d89828701614361565b828401525050610200808401358381111561466757600080fd5b61467389828701613d10565b828401525050610220808401358381111561468d57600080fd5b61469989828701613d10565b82840152505061024080840135838111156146b357600080fd5b6146bf89828701613363565b82840152505061026080840135838111156146d957600080fd5b6146e589828701613363565b82840152505061028080840135838111156146ff57600080fd5b61470b89828701613363565b8284015250506102a0808401358381111561472557600080fd5b61473189828701613363565b8284015250508093505050509250929050565b60008060006060848603121561475957600080fd5b8335925061476960208501612b59565b915060408401356001600160401b0381111561478457600080fd5b6132668682870161419b565b600080604083850312156147a357600080fd5b823591506147b3602084016134ad565b90509250929050565b60008060008060008060c087890312156147d557600080fd5b863595506147e560208801612b59565b945060408701356001600160401b0381111561480057600080fd5b61480c89828a01613d10565b945050606087013592506080870135915060a087013590509295509295509295565b60008060006060848603121561484357600080fd5b8335925061485360208501612b59565b915060408401356001600160401b0381111561486e57600080fd5b61326686828701614291565b600080600080600080600060e0888a03121561489557600080fd5b873596506148a560208901612b59565b955060408801356001600160401b03808211156148c157600080fd5b6148cd8b838c01612e34565b965060608a01359150808211156148e357600080fd5b6148ef8b838c01612fff565b955060808a013591508082111561490557600080fd5b6149118b838c01612fff565b945060a08a013591508082111561492757600080fd5b506149348a828b01612f12565b92505060c0880135905092959891949750929550565b60008060006060848603121561495f57600080fd5b8335925061496f60208501612b59565b915060408401356001600160401b0381111561498a57600080fd5b61326686828701614361565b634e487b7160e01b600052603260045260246000fd5b60008151808452602080850194506020840160005b838110156149e157815161ffff16875295820195908201906001016149c1565b509495945050505050565b60008151808452602080850194506020840160005b838110156149e157815160ff1687529582019590820190600101614a01565b61ffff85168152608060208201526000614a3d60808301866149ac565b8281036040840152614a4f81866149ec565b90508281036060840152614a6381856149ac565b979650505050505050565b600082825180855260208086019550808260051b8401018186016000805b85811015614adf57868403601f19018a52825180518086529086019086860190845b81811015614aca57835183529288019291880191600101614aae565b50509a86019a94505091840191600101614a8c565b509198975050505050505050565b60008151808452602080850194506020840160005b838110156149e1578151151587529582019590820190600101614b02565b60008282518085526020808601955060208260051b8401016020860160005b8481101561335657601f19868403018952614b5b838351614aed565b98840198925090830190600101614b3f565b600082825180855260208086019550808260051b8401018186016000805b85811015614adf57868403601f19018a52825180518086529086019086860190845b81811015614bd25783516001600160401b031683529288019291880191600101614bad565b50509a86019a94505091840191600101614b8b565b61ffff83168152604060208201526000825160e06040840152614c0e6101208401826149ac565b90506020840151603f1980858403016060860152614c2c83836149ec565b92506040860151915080858403016080860152614c4983836149ac565b925060608601519150808584030160a0860152614c668383614a6e565b925060808601519150808584030160c0860152614c838383614b20565b925060a08601519150808584030160e0860152614ca08383614b6d565b925060c08601519150808584030161010086015250614cbf8282614b6d565b9695505050505050565b634e487b7160e01b600052602160045260246000fd5b60068110614cfd57634e487b7160e01b600052602160045260246000fd5b9052565b61ffff8416815260006020606081840152845160a06060850152614d296101008501826149ac565b905081860151605f1980868403016080870152614d4683836149ec565b925060408801519150808684030160a0870152614d6383836149ac565b925060608801519150808684030160c0870152614d808383614a6e565b608089015187820390920160e08801528151808252909350908401915083830190600581901b840185016000805b83811015614e0457868303601f19018552855180518085529089019089850190845b81811015614def578351600190810b8452938c0193928c019201614dd0565b50509689019695890195935050600101614dae565b5050809650505050505050614e1c6040830184614cdf565b949350505050565b634e487b7160e01b600052601160045260246000fd5b600060ff821660ff8103614e5057614e50614e24565b60010192915050565b600082825180855260208086019550808260051b8401018186016000805b85811015614adf57868403601f19018a52825180518086529086019086860190845b81811015614ebe5783516001600160a01b031683529288019291880191600101614e99565b50509a86019a94505091840191600101614e77565b61ffff8316815260006020604081840152835160c06040850152614efb6101008501826149ac565b905081850151603f1980868403016060870152614f1883836149ec565b92506040870151915080868403016080870152614f3583836149ac565b925060608701519150808684030160a0870152614f528383614e59565b6080880151878203830160c089015280518083529194508501925084840190600581901b850186016000805b83811015614fda57878303601f1901855286518051808552908a01908a850190845b81811015614fc55783516001600160581b03168352928c0192918c0191600101614fa0565b5050978a0197958a0195935050600101614f7e565b505060a08a01519650838982030160e08a0152614ff78188614b20565b9b9a5050505050505050505050565b61ffff83168152604060208201526000825160c0604084015261502d6101008401826149ac565b90506020840151603f198085840301606086015261504b83836149ec565b9250604086015191508085840301608086015261506883836149ac565b925060608601519150808584030160a08601526150858383614e59565b925060808601519150808584030160c08601526150a28383614b20565b925060a08601519150808584030160e086015250614cbf8282614b20565b60008151808452602080850194506020840160005b838110156149e157815163ffffffff16875295820195908201906001016150d5565b61ffff83168152604060208201526000825160c0604084015261511e6101008401826149ac565b90506020840151603f198085840301606086015261513c83836149ec565b9250604086015191508085840301608086015261515983836149ac565b925060608601519150808584030160a086015261517683836150c0565b925060808601519150808584030160c086015261519383836150c0565b925060a08601519150808584030160e086015250614cbf8282614aed565b60008282518085526020808601955060208260051b8401016020860160005b8481101561335657601f198684030189526151ec8383516149ec565b988401989250908301906001016151d0565b60008151808452602080850194506020840160005b838110156149e157815160030b87529582019590820190600101615213565b60008282518085526020808601955060208260051b8401016020860160005b8481101561335657601f1986840301895261526d8383516151fe565b98840198925090830190600101615251565b60008282518085526020808601955060208260051b8401016020860160005b8481101561335657601f198684030189526152ba8383516150c0565b9884019892509083019060010161529e565b61ffff8316815260406020820152600082516101a08060408501526152f56101e08501836149ac565b91506020850151603f198086850301606087015261531384836149ec565b9350604087015191508086850301608087015261533084836149ac565b935060608701519150808685030160a087015261534d84836151b1565b935060808701519150808685030160c087015261536a8483615232565b935060a08701519150808685030160e087015261538784836150c0565b935060c087015191506101008187860301818801526153a685846151fe565b945060e088015192506101208288870301818901526153c586856151fe565b955081890151935061014091508288870301828901526153e586856151fe565b95508089015193505061016082888703018189015261540486856151fe565b9550818901519350610180915082888703018289015261542486856151fe565b9550808901519350508187860301848801526154408584614e59565b94508088015193505080868503016101c08701525050614cbf828261527f565b61ffff8316815260006020604081840152835160a0604085015261548760e08501826149ac565b905081850151603f19808684030160608701526154a483836149ec565b925060408701519150808684030160808701526154c183836149ac565b925060608701519150808684030160a08701526154de83836149ec565b608088015187820390920160c08801528151808252909350908401915083830190600581901b8401850160005b8281101561553957601f198683030184526155278286516149ac565b9487019493870193915060010161550b565b509998505050505050505050565b61ffff83168152604060208201526000825160c0604084015261556e6101008401826149ac565b90506020840151603f198085840301606086015261558c83836149ec565b925060408601519150808584030160808601526155a983836149ac565b925060608601519150808584030160a08601526155c68383614b20565b925060808601519150808584030160c08601526155e383836151b1565b925060a08601519150808584030160e086015250614cbf8282615232565b61ffff83168152604060208201526000825160c060408401526156286101008401826149ac565b90506020840151603f198085840301606086015261564683836149ec565b9250604086015191508085840301608086015261566383836149ac565b925060608601519150808584030160a086015261568083836151b1565b925060808601519150808584030160c08601526150a28383614b6d565b61ffff831681526040602082015260008251606060408401526156c360a08401826149ac565b90506020840151603f19808584030160608601526156e183836149ec565b9250604086015191508085840301608086015250614cbf828261527f565b60008261571c57634e487b7160e01b600052601260045260246000fd5b500490565b8082018082111561239457612394614e24565b808202811582820484141761239457612394614e24565b8181038181111561239457612394614e24565b634e487b7160e01b600052603160045260246000fdfea26469706673582212207929ebae6538389b539c782b6d5ea3a897e3ab30cf46ac02dcd4e1ef7d4486cb64736f6c63430008170033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 31 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.