Source Code
Overview
S Balance
S Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
AirdropDistributor
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/IController.sol";
import "../interfaces/IERC20.sol";
import "../openzeppelin/EnumerableMap.sol";
import "../proxy/Controllable.sol";
import "../relay/ERC2771Context.sol";
import "../openzeppelin/MerkleProof.sol";
import {IApplicationEvents} from "../interfaces/IApplicationEvents.sol";
contract AirdropDistributor is Controllable, ERC2771Context {
using EnumerableMap for EnumerableMap.UintToUintMap;
using EnumerableSet for EnumerableSet.AddressSet;
//region ------------------------ Constants
/// @notice Version of the contract
string public constant override VERSION = "1.0.1";
//endregion ------------------------ Constants
//region ------------------------ Members
/// @notice List of roots of registered merkle trees: week => merkleRoot
EnumerableMap.UintToUintMap internal _merkleRoots;
address public airdropToken;
/// @notice key = keccak256(abi.encode(week, merkleRoot))
mapping(bytes32 key => mapping (address user => bool isClaimed)) internal _claimed;
/// @notice List of allowed operators
EnumerableSet.AddressSet internal _operators;
//endregion ------------------------ Members
//region ------------------------ Restrictions
function onlyDeployerOrOperator(IController controller_) internal view {
address sender = _msgSender();
if (
!controller_.isDeployer(sender)
&& !_operators.contains(sender)
&& sender != controller_.governance()
) revert IAppErrors.ErrorNotAllowedSender();
}
//endregion ------------------------ Restrictions
//region ------------------------ Initialization
function init(address controller_) external initializer {
__Controllable_init(controller_);
}
//endregion ------------------------ Initialization
//region ------------------------ View
function merkleRootsLength() external view returns (uint) {
return _merkleRoots.length();
}
function merkleRoot(uint index) external view returns (uint week, bytes32 _merkleRoot) {
(uint key, uint value) = _merkleRoots.at(index);
return (key, bytes32(value));
}
function allMerkleRoots() external view returns (uint[] memory week, bytes32[] memory merkleRoots) {
uint len = _merkleRoots.length();
week = new uint[](len);
merkleRoots = new bytes32[](len);
for (uint i; i < len; ++i) {
(uint key, uint value) = _merkleRoots.at(i);
(week[i], merkleRoots[i]) = (key, bytes32(value));
}
}
/// @notice Try to verify using the merkle tree with the given {indexTree}
function verify(bytes32[] memory proof, address user, uint256 amount, uint week) public view returns (bool) {
return _verify(proof, user, amount, bytes32(_merkleRoots.get(week)));
}
/// @dev It doesn't allow to return claimed value for removed tree until it's re-registered
function claimed(uint week, address user) external view returns (bool) {
(bool exist, uint value) = _merkleRoots.tryGet(week);
return exist ? _claimed[_key(week, bytes32(value))][user] : false;
}
function allOperators() external view returns (address[] memory operators) {
return _operators.values();
}
//endregion ------------------------ View
//region ------------------------ Governance actions
function setAirdropToken(address token_) external {
onlyDeployerOrOperator(IController(controller()));
airdropToken = token_;
emit IApplicationEvents.AirdropDistributorSetToken(token_);
}
function addTree(uint week, bytes32 merkleRoot_) external {
onlyDeployerOrOperator(IController(controller()));
if (_merkleRoots.contains(week)) revert IAppErrors.AlreadyRegistered();
_merkleRoots.set(week, uint(merkleRoot_));
emit IApplicationEvents.AirdropDistributorAddTree(week, merkleRoot_);
}
function removeTree(uint week) external {
onlyDeployerOrOperator(IController(controller()));
if (!_merkleRoots.contains(week)) revert IAppErrors.NotInitialized();
_merkleRoots.remove(week);
// _claimed is not cleared - it can be used if the tree will be re-registered
emit IApplicationEvents.AirdropDistributorRemoveTree(week);
}
function setOperator(address operator_, bool remove) external {
onlyDeployerOrOperator(IController(controller()));
if (remove) {
_operators.remove(operator_);
} else {
_operators.add(operator_);
}
emit IApplicationEvents.SetOperator(operator_, remove);
}
//endregion ------------------------ Governance actions
//region ------------------------ Main logic
/// @notice The signer claims {amounts} for the given {weeks}
/// @param proofs_ Merkle proofs generated by full tree
/// @param receiver_ Transfer claimed amounts on the given address
function claim(bytes32[][] memory proofs_, uint[] memory weeks_, uint[] memory amounts_, address receiver_) external {
address user = _msgSender();
address _airdropToken = airdropToken;
uint len = proofs_.length;
if (len != weeks_.length || len != amounts_.length) revert IAppErrors.LengthsMismatch();
for (uint i; i < len; ++i) {
(bool exist, uint _merkleRoot) = _merkleRoots.tryGet(weeks_[i]);
if (!exist) revert IAppErrors.ZeroValue();
if (!_verify(proofs_[i], user, amounts_[i], bytes32(_merkleRoot))) revert IAppErrors.InvalidProof();
bytes32 key = _key(weeks_[i], bytes32(_merkleRoot));
if (_claimed[key][user]) revert IAppErrors.AlreadyClaimed();
_claimed[key][user] = true;
IERC20(_airdropToken).transfer(receiver_, amounts_[i]);
}
emit IApplicationEvents.AirdropDistributorClaim(weeks_, amounts_, receiver_);
}
function salvage(address receiver_, address token_, uint amount_) external {
onlyDeployerOrOperator(IController(controller()));
IERC20(token_).transfer(receiver_, amount_);
emit IApplicationEvents.Salvage(receiver_, token_, amount_);
}
//endregion ------------------------ Main logic
//region ------------------------ Internal logic
function _key(uint week, bytes32 merkleRoot_) internal pure returns (bytes32) {
return keccak256(abi.encode(week, merkleRoot_));
}
function _verify(bytes32[] memory proof, address addr, uint256 amount, bytes32 merkleRoot_) internal pure returns (bool) {
bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(addr, amount))));
return MerkleProof.verify(proof, merkleRoot_, leaf);
}
//endregion ------------------------ Internal logic
}// 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);
error AlreadyClaimed();
//region Restrictions
error ErrorNotDeployer(address sender);
error ErrorNotGoc();
error NotGovernance(address sender);
error ErrorOnlyEoa();
error NotEOA(address sender);
error ErrorForbidden(address sender);
error AdminOnly();
error ErrorNotItemController(address sender);
error ErrorNotHeroController(address sender);
error ErrorNotDungeonFactory(address sender);
error ErrorNotObjectController(address sender);
error ErrorNotStoryController();
error ErrorNotAllowedSender();
error MintNotAllowed();
//endregion Restrictions
//region PackingLib
error TooHighValue(uint value);
error IntValueOutOfRange(int value);
error OutOfBounds(uint index, uint length);
error UnexpectedValue(uint expected, uint actual);
error WrongValue(uint newValue, uint actual);
error IntOutOfRange(int value);
error ZeroValue();
/// @notice packCustomDataChange requires an input string with two zero bytes at the beginning
/// 0xXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX0000
/// This error happens if these bytes are not zero
error IncompatibleInputString();
error IncorrectOtherItemTypeKind(uint8 kind);
//endregion PackingLib
//region Hero
error ErrorHeroIsNotRegistered(address heroToken);
error ErrorHeroIsDead(address heroToken, uint heroTokenId);
error ErrorHeroNotInDungeon();
error HeroInDungeon();
error ErrorNotOwner(address token, uint tokenId);
error Staked(address heroToken, uint heroId);
error NameTaken();
error TooBigName();
error WrongSymbolsInTheName();
error NoPayToken(address token, uint payTokenAmount);
error AlreadyHaveReinforcement();
/// @notice SIP-001 - Reinforcement requires 3 skills
error ErrorReinforcementRequiresThreeSkills();
error WrongTier(uint tier);
error NotEnoughNgLevel(uint8 ngLevel);
error NgpNotActive(address hero);
error RebornNotAllowed();
error AlreadyPrePaidHero();
//endregion Hero
//region Dungeon
error ErrorDungeonIsFreeAlready();
error ErrorNoEligibleDungeons();
error ErrorDungeonBusy();
error ErrorNoDungeonsForBiome(uint8 heroBiome);
error ErrorDungeonCompleted();
error ErrorAlreadyInDungeon();
error NotEnoughTokens(uint balance, uint expectedBalance);
error DungeonAlreadySpecific(uint16 dungNum);
error DungeonAlreadySpecific2(uint16 dungNum);
error WrongSpecificDungeon();
//endregion Dungeon
//region Items
error ErrorItemNotEligibleForTheSlot(uint itemType, uint8 itemSlot);
error ErrorItemSlotBusyHand(uint8 slot);
error ErrorItemSlotBusy();
error ErrorItemNotInSlot();
error ErrorConsumableItemIsUsed(address item);
error ErrorCannotRemoveItemFromMap();
error ErrorCannotRemoveDataFromMap();
error EquippedItemsExist();
error ItemEquipped(address item, uint itemId);
error ZeroItemMetaType();
error NotZeroOtherItemMetaType();
error ZeroLevel();
error ItemTypeChanged();
error ItemMetaTypeChanged();
error UnknownItem(address item);
error ErrorEquipForbidden();
error EquipForbiddenInDungeon();
error TakeOffForbiddenInDungeon();
error Consumable(address item);
error NotConsumable(address item);
error Broken(address item);
error ZeroLife();
error RequirementsToItemAttributes();
error NotEquipped(address item);
error ZeroDurability();
error ZeroAugmentation();
error TooHighAgLevel(uint8 augmentationLevel);
error UseForbiddenZeroPayToken();
error IncorrectMinMaxAttributeRange(int32 min, int32 max);
error SameIdsNotAllowed();
error ZeroFragility();
error OtherTypeItemNotRepairable();
error NotOther();
error DoubleItemUsageForbidden(uint itemIndex, address[] items);
error ItemAlreadyUsedInSlot(address item, uint8 equippedSlot);
error WrongWayToRegisterItem();
error UnionItemNotFound(address item);
error WrongListUnionItemTokens(address item, uint countTokens, uint requiredCountTokens);
error UnknownUnionConfig(uint unionConfigId);
error UserHasNoKeyPass(address user, address keyPassItem);
error MaxValue(uint value);
error UnexpectedOtherItem(address item);
error NotExist();
//endregion Items
//region Stages
error ErrorWrongStage(uint stage);
error ErrorNotStages();
//endregion Stages
//region Level
error ErrorWrongLevel(uint heroLevel);
error ErrorLevelTooLow(uint heroLevel);
error ErrorHeroLevelStartFrom1();
error ErrorWrongLevelUpSum();
error ErrorMaxLevel();
//endregion Level
//region Treasure
error ErrorNotValidTreasureToken(address treasureToken);
//endregion Treasure
//region State
error ErrorPaused();
error ErrorNotReady();
error ErrorNotObject1();
error ErrorNotObject2();
error ErrorNotCompleted();
//endregion State
//region Biome
error ErrorNotBiome();
error ErrorIncorrectBiome(uint biome);
error TooHighBiome(uint biome);
//endregion Biome
//region Misc
error ErrorWrongMultiplier(uint multiplier);
error ErrorNotEnoughMana(uint32 mana, uint requiredMana);
error ErrorExperienceMustNotDecrease();
error ErrorNotEnoughExperience();
error ErrorNotChances();
error ErrorNotEligible(address heroToken, uint16 dungNum);
error ErrorZeroKarmaNotAllowed();
//endregion Misc
//region GOC
error GenObjectIdBiomeOverflow(uint8 biome);
error GenObjectIdSubTypeOverflow(uint subType);
error GenObjectIdIdOverflow(uint id);
error UnknownObjectTypeGoc1(uint8 objectType);
error UnknownObjectTypeGoc2(uint8 objectType);
error UnknownObjectTypeGocLib1(uint8 objectType);
error UnknownObjectTypeGocLib2(uint8 objectType);
error UnknownObjectTypeForSubtype(uint8 objectSubType);
error FightDelay();
error ZeroChance();
error TooHighChance(uint32 chance);
error TooHighRandom(uint random);
error EmptyObjects();
error ObjectNotFound();
error WrongGetObjectTypeInput();
error WrongChances(uint32 chances, uint32 maxChances);
//endregion GOC
//region Story
error PageNotRemovedError(uint pageId);
error NotItem1();
error NotItem2();
error NotRandom(uint32 random);
error NotHeroData();
error NotGlobalData();
error ZeroStoryIdRemoveStory();
error ZeroStoryIdStoryAction();
error ZeroStoryIdAction();
error NotEnoughAmount(uint balance, uint requiredAmount);
error NotAnswer();
error AnswerStoryIdMismatch(uint16 storyId, uint16 storyIdFromAnswerHash);
error AnswerPageIdMismatch(uint16 pageId, uint16 pageIdFromAnswerHash);
//endregion Story
//region FightLib
error NotMagic();
error NotAType(uint atype);
//endregion FightLib
//region MonsterLib
error NotYourDebuffItem();
error UnknownAttackType(uint attackType);
error NotYourAttackItem();
/// @notice The skill item cannot be used because it doesn't belong either to the hero or to the hero's helper
error NotYourBuffItem();
//endregion MonsterLib
//region GameToken
error ApproveToZeroAddress();
error MintToZeroAddress();
error TransferToZeroAddress();
error TransferAmountExceedsBalance(uint balance, uint value);
error InsufficientAllowance();
error BurnAmountExceedsBalance();
error NotMinter(address sender);
//endregion GameToken
//region NFT
error TokenTransferNotAllowed();
error IdOverflow(uint id);
error NotExistToken(uint tokenId);
error EquippedItemIsNotAllowedToTransfer(uint tokenId);
//endregion NFT
//region CalcLib
error TooLowX(uint x);
//endregion CalcLib
//region Controller
error NotFutureGovernance(address sender);
//endregion Controller
//region Oracle
error OracleWrongInput();
//endregion Oracle
//region ReinforcementController
error AlreadyStaked();
error MaxFee(uint8 fee);
error MinFee(uint8 fee);
error StakeHeroNotStats();
error NotStaked();
error NoStakedHeroes();
error GuildHelperNotAvailable(uint guildId, address helper, uint helperId);
error HelperNotAvailableInGivenBiome();
//endregion ReinforcementController
//region SponsoredHero
error InvalidHeroClass();
error ZeroAmount();
error InvalidProof();
error NoHeroesAvailable();
error AlreadyRegistered();
//endregion SponsoredHero
//region SacraRelay
error SacraRelayNotOwner();
error SacraRelayNotDelegator();
error SacraRelayNotOperator();
error SacraRelayInvalidChainId(uint callChainId, uint blockChainId);
error SacraRelayInvalidNonce(uint callNonce, uint txNonce);
error SacraRelayDeadline();
error SacraRelayDelegationExpired();
error SacraRelayNotAllowed();
error SacraRelayInvalidSignature();
/// @notice This error is generated when custom error is caught
/// There is no info about custom error in SacraRelay
/// but you can decode custom error by selector, see tests
error SacraRelayNoErrorSelector(bytes4 selector, string tracingInfo);
/// @notice This error is generated when custom error is caught
/// There is no info about custom error in SacraRelay
/// but you can decode custom error manually from {errorBytes} as following:
/// if (keccak256(abi.encodeWithSignature("MyError()")) == keccak256(errorBytes)) { ... }
error SacraRelayUnexpectedReturnData(bytes errorBytes, string tracingInfo);
error SacraRelayCallToNotContract(address notContract, string tracingInfo);
//endregion SacraRelay
//region Misc
error UnknownHeroClass(uint heroClass);
error AbsDiff(int32 a, int32 b);
//region Misc
//region ------------------------ UserController
error NoAvailableLootBox(address msgSender, uint lootBoxKind);
error FameHallHeroAlreadyRegistered(uint8 openedNgLevel);
//endregion ------------------------ UserController
//region ------------------------ Guilds
error AlreadyGuildMember();
error NotGuildMember();
error WrongGuild();
error GuildActionForbidden(uint right);
error GuildHasMaxSize(uint guildSize);
error GuildHasMaxLevel(uint level);
error TooLongUrl();
error TooLongDescription();
error CannotRemoveGuildOwnerFromNotEmptyGuild();
error GuildControllerOnly();
error GuildAlreadyHasShelter();
error ShelterIsBusy();
error ShelterIsNotRegistered();
error ShelterIsNotOwnedByTheGuild();
error ShelterIsInUse();
error GuildHasNoShelter();
error ShelterBidIsNotAllowedToBeUsed();
error ShelterHasHeroesInside();
error SecondGuildAdminIsNotAllowed();
error NotEnoughGuildBankBalance(uint guildId);
error GuildReinforcementCooldownPeriod();
error NoStakedGuildHeroes();
error NotStakedInGuild();
error ShelterHasNotEnoughLevelForReinforcement();
error NotBusyGuildHelper();
error GuildRequestNotActive();
error GuildRequestNotAvailable();
error NotAdminCannotAddMemberWithNotZeroRights();
//endregion ------------------------ Guilds
//region ------------------------ Shelters
error ErrorNotShelterController();
error ErrorNotGuildController();
error ShelterHasNotItem(uint shelterId, address item);
error MaxNumberItemsSoldToday(uint numSoldItems, uint limit);
error GuildHasNotEnoughPvpPoints(uint64 pointsAvailable, uint pointRequired);
error FreeShelterItemsAreNotAllowed(uint shelterId, address item);
error TooLowShelterLevel(uint8 shelterLevel, uint8 allowedShelterLevel);
error NotEnoughPvpPointsCapacity(address user, uint usedPoints, uint pricePvpPoints, uint64 capactiy);
error IncorrectShelterLevel(uint8 shelterLevel);
//endregion ------------------------ Shelters
//region ------------------------ Auction
error WrongAuctionPosition();
error AuctionPositionClosed();
error AuctionBidOpened(uint positionId);
error TooLowAmountToBid();
error AuctionEnded();
error TooLowAmountForNewBid();
error AuctionSellerOnly();
error AuctionBuyerOnly();
error AuctionBidNotFound();
error AuctionBidClosed();
error OnlyShelterAuction();
error CannotCloseLastBid();
error AuctionNotEnded();
error NotShelterAuction();
error AuctionPositionOpened(uint positionId);
error AuctionSellerCannotBid();
error AuctionGuildWithShelterCannotBid();
error AuctionBidExists();
//endregion ------------------------ Auction
//region ------------------------ Pawnshop
error AuctionPositionNotSupported(uint positionId);
error PositionNotSupported(uint positionId);
error NotNftPositionNotSupported(uint positionId);
error CallFailed(bytes callResultData);
error PawnShopZeroOwner();
error PawnShopZeroFeeRecipient();
error PawnShopNotOwner();
error PawnShopAlreadyAnnounced();
error PawnShopTimeLock();
error PawnShopWrongAddressValue();
error PawnShopWrongUintValue();
error PawnShopZeroAddress();
error PawnShopTooHighValue();
error PawnShopZeroAToken();
error PawnShopZeroCToken();
error PawnShopWrongAmounts();
error PawnShopPosFeeForInstantDealForbidden();
error PawnShopPosFeeAbsurdlyHigh();
error PawnShopIncorrect();
error PawnShopWrongId();
error PawnShopNotBorrower();
error PawnShopPositionClosed();
error PawnShopPositionExecuted();
error PawnShopWrongBidAmount();
error PawnShopTooLowBid();
error PawnShopNewBidTooLow();
error PawnShopBidAlreadyExists();
error PawnShopAuctionEnded();
error PawnShopNotLender();
error PawnShopTooEarlyToClaim();
error PawnShopPositionNotExecuted();
error PawnShopAlreadyClaimed();
error PawnShopAuctionNotEnded();
error PawnShopBidClosed();
error PawnShopNoBids();
error PawnShopAuctionBidNotFound();
error PawnShopWrongBid();
error PawnShopBidNotFound();
//endregion ------------------------ Pawnshop
}// 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 ------------------------ Common
event SetOperator(address operator, bool remove);
event Salvage(address receiver, address token, uint amount);
//endregion ------------------------ Common
//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
//region ------------------------ Airdrop Distributor
event AirdropDistributorSetToken(address token);
event AirdropDistributorAddTree(uint week, bytes32 merkleRoot_);
event AirdropDistributorRemoveTree(uint week);
event AirdropDistributorClaim(uint[] _weeks, uint[] amounts, address receiver);
//endregion ------------------------ Airdrop Distributor
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;
interface IControllable {
function VERSION() external pure returns (string memory);
function revision() external view returns (uint);
function previousImplementation() external view returns (address);
function isController(address contract_) external view returns (bool);
function isGovernance(address contract_) external view returns (bool);
function created() external view returns (uint256);
function createdBlock() external view returns (uint256);
function controller() external view returns (address);
function increaseRevision(address oldLogic) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;
interface IController {
function 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;
/// @notice NG_LEVEL of the hero that has created the given dungeon
mapping(uint64 dungeonId => uint ngLevel) dungeonNgLevel;
}
struct ObjectGenerateInfo {
/// @notice List of chamber types for each unique object
/// @dev uint8 types, packed using PackingLib.packUint8Array
bytes32[] objTypesByStages;
/// @notice List of chances for each chamber type
/// @dev uint64 chances
uint32[][] objChancesByStages;
}
struct DungeonGenerateInfo {
/// @notice List of chamber types for each unique object
uint8[][] objTypesByStages;
/// @notice List of chances for each chamber type
uint32[][] objChancesByStages;
uint32[] uniqObjects;
uint8 minLevel;
uint8 maxLevel;
bytes32[] requiredCustomDataIndex;
uint64[] requiredCustomDataMinValue;
uint64[] requiredCustomDataMaxValue;
bool[] requiredCustomDataIsHero;
}
/// @notice Attributes of the given dungeon logic
struct DungeonAttributes {
/// @notice Total number of stages that should be passed to complete the dungeon
uint8 stages;
uint8 biome;
/// @notice Default list of objects that should be passed in the dungeon
uint32[] uniqObjects;
/// @dev min+max (packUint8Array)
bytes32 minMaxLevel;
bytes32[] requiredCustomDataIndex;
/// @notice Packed DungeonGenerateInfo.requiredCustomData: MinValue, MaxValue, IsHero
/// @dev min+max+isHero(packStoryCustomDataRequirements)
bytes32[] requiredCustomDataValue;
ObjectGenerateInfo info;
}
/// @notice Current status of the given dungeon
struct DungeonStatus {
uint64 dungeonId;
/// @notice Dungeon logic id
uint16 dungNum;
/// @notice True if the dungeon is completed by the hero
bool isCompleted;
/// @notice Hero in the dungeon or 0
address heroToken;
uint heroTokenId;
/// @notice Current object that should be passed by the hero. 0 - new object is not opened
uint32 currentObject;
/// @notice Current stage in the dungeon that should be passed by the hero.
uint8 currentStage;
EnumerableMap.AddressToUintMap treasuryTokens;
/// @notice All items that were minted on result of made actions
bytes32[] treasuryItems;
/// @notice Total number of stages that should be passed to complete the dungeon
/// This value can be bigger than length of uniqObjects
uint8 stages;
/// @notice List of objects to be passed in the stage. The list can be dynamically changed during passing the stages
uint32[] uniqObjects;
}
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
function launchForNewHero(address heroToken, uint heroTokenId, address owner) external returns (uint64 dungeonId);
function maxBiomeCompleted(address heroToken, uint heroTokenId) external view returns (uint8);
function currentDungeon(address heroToken, uint heroTokenId) external view returns (uint64);
function skillSlotsForDurabilityReduction(address heroToken, uint heroTokenId) external view returns (uint8[] memory result);
function setBossCompleted(uint32 objectId, address heroToken, uint heroTokenId, uint8 heroBiome) external;
/// @notice Hero exists current dungeon forcibly same as when dying but without loosing life chance
function exitForcibly(address heroToken, uint heroTokenId, address msgSender) external;
function maxAvailableBiome() external view returns (uint8);
function reborn(address hero, uint heroId) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}// SPDX-License-Identifier: 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;
/// @title Library for setting / getting slot variables (used in upgradable proxy contracts)
/// @author bogdoslav
library SlotsLib {
/// @notice Version of the contract
/// @dev Should be incremented when contract changed
string public constant SLOT_LIB_VERSION = "1.0.0";
// ************* GETTERS *******************
/// @dev Gets a slot as bytes32
function getBytes32(bytes32 slot) internal view returns (bytes32 result) {
assembly {
result := sload(slot)
}
}
/// @dev Gets a slot as an address
function getAddress(bytes32 slot) internal view returns (address result) {
assembly {
result := sload(slot)
}
}
/// @dev Gets a slot as uint256
function getUint(bytes32 slot) internal view returns (uint result) {
assembly {
result := sload(slot)
}
}
// ************* ARRAY GETTERS *******************
/// @dev Gets an array length
function arrayLength(bytes32 slot) internal view returns (uint result) {
assembly {
result := sload(slot)
}
}
/// @dev Gets a slot array by index as address
/// @notice First slot is array length, elements ordered backward in memory
/// @notice This is unsafe, without checking array length.
function addressAt(bytes32 slot, uint index) internal view returns (address result) {
bytes32 pointer = bytes32(uint(slot) - 1 - index);
assembly {
result := sload(pointer)
}
}
// ************* SETTERS *******************
/// @dev Sets a slot with bytes32
/// @notice Check address for 0 at the setter
function set(bytes32 slot, bytes32 value) internal {
assembly {
sstore(slot, value)
}
}
/// @dev Sets a slot with address
/// @notice Check address for 0 at the setter
function set(bytes32 slot, address value) internal {
assembly {
sstore(slot, value)
}
}
/// @dev Sets a slot with uint
function set(bytes32 slot, uint value) internal {
assembly {
sstore(slot, value)
}
}
}// SPDX-License-Identifier: 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) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol)
pragma solidity ^0.8.20;
/**
* @dev These functions deal with verification of Merkle Tree proofs.
*
* The tree and the proofs can be generated using our
* https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
* You will find a quickstart guide in the readme.
*
* WARNING: You should avoid using leaf values that are 64 bytes long prior to
* hashing, or use a hash function other than keccak256 for hashing leaves.
* This is because the concatenation of a sorted pair of internal nodes in
* the Merkle tree could be reinterpreted as a leaf value.
* OpenZeppelin's JavaScript library generates Merkle trees that are safe
* against this attack out of the box.
*/
library MerkleProof {
/**
*@dev The multiproof provided is not valid.
*/
error MerkleProofInvalidMultiproof();
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*/
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
/**
* @dev Calldata version of {verify}
*/
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProofCalldata(proof, leaf) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leafs & pre-images are assumed to be sorted.
*/
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Calldata version of {processProof}
*/
function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function multiProofVerify(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProof(proof, proofFlags, leaves) == root;
}
/**
* @dev Calldata version of {multiProofVerify}
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function multiProofVerifyCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProofCalldata(proof, proofFlags, leaves) == root;
}
/**
* @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
* proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
* leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
* respectively.
*
* CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
* is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
* tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
*/
function processMultiProof(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
if (leavesLen + proofLen != totalHashes + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
if (proofPos != proofLen) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Calldata version of {processMultiProof}.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function processMultiProofCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
if (leavesLen + proofLen != totalHashes + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
if (proofPos != proofLen) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Sorts the pair (a, b) and hashes the result.
*/
function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
}
/**
* @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
*/
function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;
import "../openzeppelin/Initializable.sol";
import "../interfaces/IControllable.sol";
import "../interfaces/IController.sol";
import "../lib/SlotsLib.sol";
/// @title Implement basic functionality for any contract that require strict control
/// @dev Can be used with upgradeable pattern.
/// Require call __Controllable_init() in any case.
/// @author belbix
abstract contract Controllable is Initializable, IControllable {
using SlotsLib for bytes32;
/// @notice Version of the contract
/// @dev Should be incremented when contract changed
string public constant CONTROLLABLE_VERSION = "1.0.0";
bytes32 internal constant _CONTROLLER_SLOT = bytes32(uint256(keccak256("eip1967.controllable.controller")) - 1);
bytes32 internal constant _CREATED_SLOT = bytes32(uint256(keccak256("eip1967.controllable.created")) - 1);
bytes32 internal constant _CREATED_BLOCK_SLOT = bytes32(uint256(keccak256("eip1967.controllable.created_block")) - 1);
bytes32 internal constant _REVISION_SLOT = bytes32(uint256(keccak256("eip1967.controllable.revision")) - 1);
bytes32 internal constant _PREVIOUS_LOGIC_SLOT = bytes32(uint256(keccak256("eip1967.controllable.prev_logic")) - 1);
event ContractInitialized(address controller, uint ts, uint block);
event RevisionIncreased(uint value, address oldLogic);
// init implementation contract
constructor() initializer {}
/// @notice Initialize contract after setup it as proxy implementation
/// Save block.timestamp in the "created" variable
/// @dev Use it only once after first logic setup
/// @param controller_ Controller address
function __Controllable_init(address controller_) internal onlyInitializing {
_init(controller_);
}
function _init(address controller_) private {
require(controller_ != address(0), "Zero controller");
_CONTROLLER_SLOT.set(controller_);
_CREATED_SLOT.set(block.timestamp);
_CREATED_BLOCK_SLOT.set(block.number);
emit ContractInitialized(controller_, block.timestamp, block.number);
}
/// @dev Return true if given address is controller
function isController(address value_) public override view returns (bool) {
return value_ == controller();
}
/// @notice Return true if given address is setup as governance in Controller
function isGovernance(address value_) public override view returns (bool) {
return IController(controller()).governance() == value_;
}
/// @dev Contract upgrade counter
function revision() external view override returns (uint) {
return _REVISION_SLOT.getUint();
}
/// @dev Previous logic implementation
function previousImplementation() external view override returns (address) {
return _PREVIOUS_LOGIC_SLOT.getAddress();
}
// ************* SETTERS/GETTERS *******************
/// @notice Return controller address saved in the contract slot
function controller() public view override returns (address) {
return _CONTROLLER_SLOT.getAddress();
}
/// @notice Return creation timestamp
/// @return Creation timestamp
function created() external view override returns (uint256) {
return _CREATED_SLOT.getUint();
}
/// @notice Return creation block number
/// @return Creation block number
function createdBlock() external override view returns (uint256) {
return _CREATED_BLOCK_SLOT.getUint();
}
/// @dev Revision should be increased on each contract upgrade
function increaseRevision(address oldLogic) external override {
require(msg.sender == address(this), "Increase revision forbidden");
uint r = _REVISION_SLOT.getUint() + 1;
_REVISION_SLOT.set(r);
_PREVIOUS_LOGIC_SLOT.set(oldLogic);
emit RevisionIncreased(r, oldLogic);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (metatx/ERC2771Context.sol)
pragma solidity ^0.8.1;
import "../interfaces/IAppErrors.sol";
/**
* @dev Context variant with ERC2771 support.
*/
// based on https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/metatx/ERC2771Context.sol
abstract contract ERC2771Context {
// for whitelist new relayers need to add new constants and update proxies
address private constant GELATO_RELAY_1_BALANCE_ERC_2771 = 0xd8253782c45a12053594b9deB72d8e8aB2Fca54c;
address private constant SACRA_RELAY = 0x52CEba41Da235Af367bFC0b0cCd3314cb901bB5F;
address private constant SACRA_RELAY_2 = 0x102f1f556cD9C3D5f820E6920A8931657c5Da21B;
function isTrustedForwarder(address forwarder) public view virtual returns (bool){
return forwarder == GELATO_RELAY_1_BALANCE_ERC_2771 || forwarder == SACRA_RELAY || forwarder == SACRA_RELAY_2;
}
function _msgSender() internal view virtual returns (address sender) {
if (isTrustedForwarder(msg.sender)) {
// The assembly code is more direct than the Solidity version using `abi.decode`.
/// @solidity memory-safe-assembly
assembly {
sender := shr(96, calldataload(sub(calldatasize(), 20)))
}
return sender;
} else {
return msg.sender;
}
}
function _msgData() internal view virtual returns (bytes calldata) {
if (isTrustedForwarder(msg.sender)) {
return msg.data[: msg.data.length - 20];
} else {
return msg.data;
}
}
/// @notice Return true if given address is not a smart contract but a wallet address.
/// @dev It is not 100% guarantee after EIP-3074 implementation, use it as an additional check.
/// @return true if the address is a wallet.
function _isNotSmartContract() internal view returns (bool) {
return isTrustedForwarder(msg.sender) || msg.sender == tx.origin;
}
function onlyEOA() internal view {
if (!_isNotSmartContract()) {
revert IAppErrors.NotEOA(msg.sender);
}
}
}{
"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
Contract ABI
API[{"inputs":[],"name":"AlreadyClaimed","type":"error"},{"inputs":[],"name":"AlreadyRegistered","type":"error"},{"inputs":[{"internalType":"bytes32","name":"key","type":"bytes32"}],"name":"EnumerableMapNonexistentKey","type":"error"},{"inputs":[],"name":"ErrorNotAllowedSender","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidProof","type":"error"},{"inputs":[],"name":"LengthsMismatch","type":"error"},{"inputs":[],"name":"NotInitialized","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"ZeroValue","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"week","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"merkleRoot_","type":"bytes32"}],"name":"AirdropDistributorAddTree","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"_weeks","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"}],"name":"AirdropDistributorClaim","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"week","type":"uint256"}],"name":"AirdropDistributorRemoveTree","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"AirdropDistributorSetToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"controller","type":"address"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"block","type":"uint256"}],"name":"ContractInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"address","name":"oldLogic","type":"address"}],"name":"RevisionIncreased","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Salvage","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"remove","type":"bool"}],"name":"SetOperator","type":"event"},{"inputs":[],"name":"CONTROLLABLE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"week","type":"uint256"},{"internalType":"bytes32","name":"merkleRoot_","type":"bytes32"}],"name":"addTree","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"airdropToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allMerkleRoots","outputs":[{"internalType":"uint256[]","name":"week","type":"uint256[]"},{"internalType":"bytes32[]","name":"merkleRoots","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allOperators","outputs":[{"internalType":"address[]","name":"operators","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[][]","name":"proofs_","type":"bytes32[][]"},{"internalType":"uint256[]","name":"weeks_","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts_","type":"uint256[]"},{"internalType":"address","name":"receiver_","type":"address"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"week","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"claimed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"controller","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"created","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"createdBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"oldLogic","type":"address"}],"name":"increaseRevision","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"controller_","type":"address"}],"name":"init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"value_","type":"address"}],"name":"isController","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"value_","type":"address"}],"name":"isGovernance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"forwarder","type":"address"}],"name":"isTrustedForwarder","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"merkleRoot","outputs":[{"internalType":"uint256","name":"week","type":"uint256"},{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"merkleRootsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"previousImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"week","type":"uint256"}],"name":"removeTree","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"revision","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver_","type":"address"},{"internalType":"address","name":"token_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"salvage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token_","type":"address"}],"name":"setAirdropToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator_","type":"address"},{"internalType":"bool","name":"remove","type":"bool"}],"name":"setOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"week","type":"uint256"}],"name":"verify","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]Contract Creation Code
608060405234801561001057600080fd5b507ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff1615906001600160401b031660008115801561005b5750825b90506000826001600160401b031660011480156100775750303b155b905081158015610085575080155b156100a35760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b031916600117855583156100d157845460ff60401b1916680100000000000000001785555b831561011757845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050611bf88061012b6000396000f3fe608060405234801561001057600080fd5b50600436106101435760003560e01c8063558a7297116100be578063558a729714610252578063572b6c05146102655780637cc96380146102785780637d4584671461028057806385d4773c1461029557806392b8f2a0146102a8578063936725ec146102bb5780639d0bcca0146102ec578063b429afeb146102f4578063c15f56d514610307578063dee1f0e41461031d578063f77c479114610330578063ffa1ad741461033857600080fd5b80630c06a725146101485780631113ef521461015d578063120aa8771461017057806319ab453c14610198578063325a19f1146101ab5780633c70b357146101c15780633e36d9cb146101e95780634593144c146101fc57806347bdca6d146102045780634d06068a146102175780634e60f557146102375780634fac6ccd1461023f575b600080fd5b61015b6101563660046115fe565b61035c565b005b61015b61016b36600461163c565b6103d7565b61018361017e36600461167d565b6104a4565b60405190151581526020015b60405180910390f35b61015b6101a63660046116ad565b610506565b6101b3610600565b60405190815260200161018f565b6101d46101cf3660046115fe565b610639565b6040805192835260208301919091520161018f565b61015b6101f73660046117a2565b610654565b6101b36108d5565b61015b6102123660046116ad565b610905565b60035461022a906001600160a01b031681565b60405161018f91906118aa565b6101b361095b565b61015b61024d3660046116ad565b610967565b61015b6102603660046118cc565b610a79565b6101836102733660046116ad565b610ae8565b6101b3610b5d565b610288610b7b565b60405161018f91906118fa565b6101836102a3366004611947565b610b87565b61015b6102b63660046119a5565b610ba7565b6102df604051806040016040528060058152602001640312e302e360dc1b81525081565b60405161018f91906119c7565b61022a610c1e565b6101836103023660046116ad565b610c4e565b61030f610c73565b60405161018f929190611a52565b61018361032b3660046116ad565b610d77565b61022a610dfc565b6102df60405180604001604052806005815260200164312e302e3160d81b81525081565b61036c610367610dfc565b610e2c565b610377600082610f5c565b610394576040516321c4e35760e21b815260040160405180910390fd5b61039f600082610f6f565b506040518181527fd18db30a9183addf2543746638b693352edbab005b932f41497eba157d6568b3906020015b60405180910390a150565b6103e2610367610dfc565b60405163a9059cbb60e01b81526001600160a01b0383169063a9059cbb906104109086908590600401611aa9565b6020604051808303816000875af115801561042f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104539190611ac2565b50604080516001600160a01b038086168252841660208201529081018290527fb6765c6b3d2cf2f13bc0964d382e86d75bbff5c50bd3dacbe8d2fca1a7a5f00e9060600160405180910390a1505050565b600080806104b28186610f7b565b91509150816104c25760006104fb565b600460006104d08784610f99565b8152602080820192909252604090810160009081206001600160a01b038816825290925290205460ff165b925050505b92915050565b6000610510610fc5565b805490915060ff600160401b82041615906001600160401b03166000811580156105375750825b90506000826001600160401b031660011480156105535750303b155b905081158015610561575080155b1561057f5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156105a957845460ff60401b1916600160401b1785555b6105b286610fe9565b83156105f857845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050565b600061063461063060017f6f55f470bdc9cb5f04223fd822021061668e4dccb43e8727b295106dc9769c8b611af5565b5490565b905090565b60008080806106488186610ffd565b90969095509350505050565b600061065e61100c565b600354865186519293506001600160a01b039091169181141580610683575084518114155b156106a15760405163586cb9e160e01b815260040160405180910390fd5b60005b81811015610890576000806106dc8984815181106106c4576106c4611b08565b60200260200101516000610f7b90919063ffffffff16565b91509150816106fe57604051637c946ed760e01b815260040160405180910390fd5b61073f8a848151811061071357610713611b08565b6020026020010151878a868151811061072e5761072e611b08565b60200260200101518460001b61102e565b61075c576040516309bde33960e01b815260040160405180910390fd5b60006107848a858151811061077357610773611b08565b60200260200101518360001b610f99565b60008181526004602090815260408083206001600160a01b038c16845290915290205490915060ff16156107cb57604051630c8d9eab60e31b815260040160405180910390fd5b60008181526004602090815260408083206001600160a01b03808c1685529252909120805460ff1916600117905589519087169063a9059cbb908a908c908890811061081957610819611b08565b60200260200101516040518363ffffffff1660e01b815260040161083e929190611aa9565b6020604051808303816000875af115801561085d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108819190611ac2565b505050508060010190506106a4565b507fcecc7460fd5a48539f3312fe07da5d77ccfa24267aa63e9162f9d21344ed583c8686866040516108c493929190611b1e565b60405180910390a150505050505050565b600061063461063060017f812a673dfca07956350df10f8a654925f561d7a0da09bdbe79e653939a14d9f1611af5565b610910610367610dfc565b600380546001600160a01b0319166001600160a01b0383161790556040517f6b068f1b6e2c97ae9a2dd950e6fc1122284bf43fe3461f178ff643d6ed659e71906103cc9083906118aa565b6000610634600061108e565b3330146109b95760405162461bcd60e51b815260206004820152601b60248201527a24b731b932b0b9b2903932bb34b9b4b7b7103337b93134b23232b760291b60448201526064015b60405180910390fd5b60006109d76106306001600080516020611ba3833981519152611af5565b6109e2906001611b5c565b9050610a0481610a016001600080516020611ba3833981519152611af5565b55565b610a3382610a0160017fbfaaa2fb63266ff27c2da975f5894955056f50419af651a81f6c5060581857e4611af5565b604080518281526001600160a01b03841660208201527ff27e2ef832a4eb8ed8ec553b875eecd44764cda95b1c24170e281539e0a869c891015b60405180910390a15050565b610a84610367610dfc565b8015610a9b57610a95600583611099565b50610aa8565b610aa66005836110ae565b505b604080516001600160a01b038416815282151560208201527f1618a22a3b00b9ac70fd5a82f1f5cdd8cb272bd0f1b740ddf7c26ab05881dd5b9101610a6d565b60006001600160a01b03821673d8253782c45a12053594b9deb72d8e8ab2fca54c1480610b3157506001600160a01b0382167352ceba41da235af367bfc0b0ccd3314cb901bb5f145b8061050057506001600160a01b03821673102f1f556cd9c3d5f820e6920a8931657c5da21b1492915050565b60006106346106306001600080516020611ba3833981519152611af5565b606061063460056110c3565b6000610b9e858585610b9985876110d0565b61102e565b95945050505050565b610bb2610367610dfc565b610bbd600083610f5c565b15610bdb57604051630ea075bf60e21b815260040160405180910390fd5b610be7600083836110dc565b5060408051838152602081018390527fb463bf17383ba19a0c778fa1f0169c370139c1b955759e8d8287ce70c79cd5899101610a6d565b600061063461063060017fbfaaa2fb63266ff27c2da975f5894955056f50419af651a81f6c5060581857e4611af5565b6000610c58610dfc565b6001600160a01b0316826001600160a01b0316149050919050565b6060806000610c82600061108e565b9050806001600160401b03811115610c9c57610c9c6116ca565b604051908082528060200260200182016040528015610cc5578160200160208202803683370190505b509250806001600160401b03811115610ce057610ce06116ca565b604051908082528060200260200182016040528015610d09578160200160208202803683370190505b50915060005b81811015610d7157600080610d248184610ffd565b91509150818160001b878581518110610d3f57610d3f611b08565b60200260200101878681518110610d5857610d58611b08565b6020908102919091010191909152525050600101610d0f565b50509091565b6000816001600160a01b0316610d8b610dfc565b6001600160a01b0316635aa6e6756040518163ffffffff1660e01b8152600401602060405180830381865afa158015610dc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dec9190611b6f565b6001600160a01b03161492915050565b600061063461063060017f5165972ef41194f06c5007493031d0b927c20741adcb74403b954009fd2c3618611af5565b6000610e3661100c565b604051631430d62960e21b81529091506001600160a01b038316906350c358a490610e659084906004016118aa565b602060405180830381865afa158015610e82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ea69190611ac2565b158015610ebb5750610eb96005826110f1565b155b8015610f3a5750816001600160a01b0316635aa6e6756040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f00573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f249190611b6f565b6001600160a01b0316816001600160a01b031614155b15610f585760405163f20e063760e01b815260040160405180910390fd5b5050565b6000610f688383611106565b9392505050565b6000610f688383611112565b6000808080610f8a868661112f565b909450925050505b9250929050565b604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0090565b610ff1611169565b610ffa81611190565b50565b6000808080610f8a86866112ad565b600061101733610ae8565b15611029575060131936013560601c90565b503390565b6000808484604051602001611044929190611aa9565b60408051601f19818403018152828252805160209182012090830152016040516020818303038152906040528051906020012090506110848684836112d8565b9695505050505050565b6000610500826112ee565b6000610f68836001600160a01b0384166112f9565b6000610f68836001600160a01b0384166113ec565b60606000610f6883611436565b6000610f688383611492565b60006110e98484846114d9565b949350505050565b6000610f68836001600160a01b0384166114f6565b6000610f68838361150e565b60008181526002830160205260408120819055610f68838361151a565b600081815260028301602052604081205481908061115e576111518585611106565b925060009150610f929050565b600192509050610f92565b611171611526565b61118e57604051631afcd79f60e31b815260040160405180910390fd5b565b6001600160a01b0381166111d85760405162461bcd60e51b815260206004820152600f60248201526e2d32b9379031b7b73a3937b63632b960891b60448201526064016109b0565b61120781610a0160017f5165972ef41194f06c5007493031d0b927c20741adcb74403b954009fd2c3618611af5565b61123642610a0160017f6f55f470bdc9cb5f04223fd822021061668e4dccb43e8727b295106dc9769c8b611af5565b61126543610a0160017f812a673dfca07956350df10f8a654925f561d7a0da09bdbe79e653939a14d9f1611af5565b604080516001600160a01b038316815242602082015243918101919091527f1a2dd071001ebf6e03174e3df5b305795a4ad5d41d8fdb9ba41dbbe236713426906060016103cc565b600080806112bb8585611540565b600081815260029690960160205260409095205494959350505050565b6000826112e5858461154c565b14949350505050565b60006105008261158f565b600081815260018301602052604081205480156113e257600061131d600183611af5565b855490915060009061133190600190611af5565b905080821461139657600086600001828154811061135157611351611b08565b906000526020600020015490508087600001848154811061137457611374611b08565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806113a7576113a7611b8c565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610500565b6000915050610500565b60006113f883836114f6565b61142e57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610500565b506000610500565b60608160000180548060200260200160405190810160405280929190818152602001828054801561148657602002820191906000526020600020905b815481526020019060010190808311611472575b50505050509050919050565b6000818152600283016020526040812054801580156114b857506114b68484611106565b155b15610f685760405163015ab34360e11b8152600481018490526024016109b0565b600082815260028401602052604081208290556110e98484611599565b60009081526001919091016020526040902054151590565b6000610f6883836114f6565b6000610f6883836112f9565b6000611530610fc5565b54600160401b900460ff16919050565b6000610f6883836115a5565b600081815b84518110156115875761157d8286838151811061157057611570611b08565b60200260200101516115cf565b9150600101611551565b509392505050565b6000610500825490565b6000610f6883836113ec565b60008260000182815481106115bc576115bc611b08565b9060005260206000200154905092915050565b60008183106115eb576000828152602084905260409020610f68565b6000838152602083905260409020610f68565b60006020828403121561161057600080fd5b5035919050565b6001600160a01b0381168114610ffa57600080fd5b803561163781611617565b919050565b60008060006060848603121561165157600080fd5b833561165c81611617565b9250602084013561166c81611617565b929592945050506040919091013590565b6000806040838503121561169057600080fd5b8235915060208301356116a281611617565b809150509250929050565b6000602082840312156116bf57600080fd5b8135610f6881611617565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715611708576117086116ca565b604052919050565b60006001600160401b03821115611729576117296116ca565b5060051b60200190565b600082601f83011261174457600080fd5b8135602061175961175483611710565b6116e0565b8083825260208201915060208460051b87010193508684111561177b57600080fd5b602086015b848110156117975780358352918301918301611780565b509695505050505050565b600080600080608085870312156117b857600080fd5b84356001600160401b03808211156117cf57600080fd5b818701915087601f8301126117e357600080fd5b813560206117f361175483611710565b82815260059290921b8401810191818101908b84111561181257600080fd5b8286015b8481101561184a5780358681111561182e5760008081fd5b61183c8e86838b0101611733565b845250918301918301611816565b509850508801359250508082111561186157600080fd5b61186d88838901611733565b9450604087013591508082111561188357600080fd5b5061189087828801611733565b92505061189f6060860161162c565b905092959194509250565b6001600160a01b0391909116815260200190565b8015158114610ffa57600080fd5b600080604083850312156118df57600080fd5b82356118ea81611617565b915060208301356116a2816118be565b6020808252825182820181905260009190848201906040850190845b8181101561193b5783516001600160a01b031683529284019291840191600101611916565b50909695505050505050565b6000806000806080858703121561195d57600080fd5b84356001600160401b0381111561197357600080fd5b61197f87828801611733565b945050602085013561199081611617565b93969395505050506040820135916060013590565b600080604083850312156119b857600080fd5b50508035926020909101359150565b60006020808352835180602085015260005b818110156119f5578581018301518582016040015282016119d9565b506000604082860101526040601f19601f8301168501019250505092915050565b60008151808452602080850194506020840160005b83811015611a4757815187529582019590820190600101611a2b565b509495945050505050565b604081526000611a656040830185611a16565b82810360208481019190915284518083528582019282019060005b81811015611a9c57845183529383019391830191600101611a80565b5090979650505050505050565b6001600160a01b03929092168252602082015260400190565b600060208284031215611ad457600080fd5b8151610f68816118be565b634e487b7160e01b600052601160045260246000fd5b8181038181111561050057610500611adf565b634e487b7160e01b600052603260045260246000fd5b606081526000611b316060830186611a16565b8281036020840152611b438186611a16565b91505060018060a01b0383166040830152949350505050565b8082018082111561050057610500611adf565b600060208284031215611b8157600080fd5b8151610f6881611617565b634e487b7160e01b600052603160045260246000fdfe22573091f17911fb166032a3d9e0554aa73d31b7b7ddea4a4dd2995650af84bda2646970667358221220d011b23aa5869c5549188dc384543d96584254ff811eaf2dcd470e210b8b244764736f6c63430008170033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101435760003560e01c8063558a7297116100be578063558a729714610252578063572b6c05146102655780637cc96380146102785780637d4584671461028057806385d4773c1461029557806392b8f2a0146102a8578063936725ec146102bb5780639d0bcca0146102ec578063b429afeb146102f4578063c15f56d514610307578063dee1f0e41461031d578063f77c479114610330578063ffa1ad741461033857600080fd5b80630c06a725146101485780631113ef521461015d578063120aa8771461017057806319ab453c14610198578063325a19f1146101ab5780633c70b357146101c15780633e36d9cb146101e95780634593144c146101fc57806347bdca6d146102045780634d06068a146102175780634e60f557146102375780634fac6ccd1461023f575b600080fd5b61015b6101563660046115fe565b61035c565b005b61015b61016b36600461163c565b6103d7565b61018361017e36600461167d565b6104a4565b60405190151581526020015b60405180910390f35b61015b6101a63660046116ad565b610506565b6101b3610600565b60405190815260200161018f565b6101d46101cf3660046115fe565b610639565b6040805192835260208301919091520161018f565b61015b6101f73660046117a2565b610654565b6101b36108d5565b61015b6102123660046116ad565b610905565b60035461022a906001600160a01b031681565b60405161018f91906118aa565b6101b361095b565b61015b61024d3660046116ad565b610967565b61015b6102603660046118cc565b610a79565b6101836102733660046116ad565b610ae8565b6101b3610b5d565b610288610b7b565b60405161018f91906118fa565b6101836102a3366004611947565b610b87565b61015b6102b63660046119a5565b610ba7565b6102df604051806040016040528060058152602001640312e302e360dc1b81525081565b60405161018f91906119c7565b61022a610c1e565b6101836103023660046116ad565b610c4e565b61030f610c73565b60405161018f929190611a52565b61018361032b3660046116ad565b610d77565b61022a610dfc565b6102df60405180604001604052806005815260200164312e302e3160d81b81525081565b61036c610367610dfc565b610e2c565b610377600082610f5c565b610394576040516321c4e35760e21b815260040160405180910390fd5b61039f600082610f6f565b506040518181527fd18db30a9183addf2543746638b693352edbab005b932f41497eba157d6568b3906020015b60405180910390a150565b6103e2610367610dfc565b60405163a9059cbb60e01b81526001600160a01b0383169063a9059cbb906104109086908590600401611aa9565b6020604051808303816000875af115801561042f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104539190611ac2565b50604080516001600160a01b038086168252841660208201529081018290527fb6765c6b3d2cf2f13bc0964d382e86d75bbff5c50bd3dacbe8d2fca1a7a5f00e9060600160405180910390a1505050565b600080806104b28186610f7b565b91509150816104c25760006104fb565b600460006104d08784610f99565b8152602080820192909252604090810160009081206001600160a01b038816825290925290205460ff165b925050505b92915050565b6000610510610fc5565b805490915060ff600160401b82041615906001600160401b03166000811580156105375750825b90506000826001600160401b031660011480156105535750303b155b905081158015610561575080155b1561057f5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156105a957845460ff60401b1916600160401b1785555b6105b286610fe9565b83156105f857845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050565b600061063461063060017f6f55f470bdc9cb5f04223fd822021061668e4dccb43e8727b295106dc9769c8b611af5565b5490565b905090565b60008080806106488186610ffd565b90969095509350505050565b600061065e61100c565b600354865186519293506001600160a01b039091169181141580610683575084518114155b156106a15760405163586cb9e160e01b815260040160405180910390fd5b60005b81811015610890576000806106dc8984815181106106c4576106c4611b08565b60200260200101516000610f7b90919063ffffffff16565b91509150816106fe57604051637c946ed760e01b815260040160405180910390fd5b61073f8a848151811061071357610713611b08565b6020026020010151878a868151811061072e5761072e611b08565b60200260200101518460001b61102e565b61075c576040516309bde33960e01b815260040160405180910390fd5b60006107848a858151811061077357610773611b08565b60200260200101518360001b610f99565b60008181526004602090815260408083206001600160a01b038c16845290915290205490915060ff16156107cb57604051630c8d9eab60e31b815260040160405180910390fd5b60008181526004602090815260408083206001600160a01b03808c1685529252909120805460ff1916600117905589519087169063a9059cbb908a908c908890811061081957610819611b08565b60200260200101516040518363ffffffff1660e01b815260040161083e929190611aa9565b6020604051808303816000875af115801561085d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108819190611ac2565b505050508060010190506106a4565b507fcecc7460fd5a48539f3312fe07da5d77ccfa24267aa63e9162f9d21344ed583c8686866040516108c493929190611b1e565b60405180910390a150505050505050565b600061063461063060017f812a673dfca07956350df10f8a654925f561d7a0da09bdbe79e653939a14d9f1611af5565b610910610367610dfc565b600380546001600160a01b0319166001600160a01b0383161790556040517f6b068f1b6e2c97ae9a2dd950e6fc1122284bf43fe3461f178ff643d6ed659e71906103cc9083906118aa565b6000610634600061108e565b3330146109b95760405162461bcd60e51b815260206004820152601b60248201527a24b731b932b0b9b2903932bb34b9b4b7b7103337b93134b23232b760291b60448201526064015b60405180910390fd5b60006109d76106306001600080516020611ba3833981519152611af5565b6109e2906001611b5c565b9050610a0481610a016001600080516020611ba3833981519152611af5565b55565b610a3382610a0160017fbfaaa2fb63266ff27c2da975f5894955056f50419af651a81f6c5060581857e4611af5565b604080518281526001600160a01b03841660208201527ff27e2ef832a4eb8ed8ec553b875eecd44764cda95b1c24170e281539e0a869c891015b60405180910390a15050565b610a84610367610dfc565b8015610a9b57610a95600583611099565b50610aa8565b610aa66005836110ae565b505b604080516001600160a01b038416815282151560208201527f1618a22a3b00b9ac70fd5a82f1f5cdd8cb272bd0f1b740ddf7c26ab05881dd5b9101610a6d565b60006001600160a01b03821673d8253782c45a12053594b9deb72d8e8ab2fca54c1480610b3157506001600160a01b0382167352ceba41da235af367bfc0b0ccd3314cb901bb5f145b8061050057506001600160a01b03821673102f1f556cd9c3d5f820e6920a8931657c5da21b1492915050565b60006106346106306001600080516020611ba3833981519152611af5565b606061063460056110c3565b6000610b9e858585610b9985876110d0565b61102e565b95945050505050565b610bb2610367610dfc565b610bbd600083610f5c565b15610bdb57604051630ea075bf60e21b815260040160405180910390fd5b610be7600083836110dc565b5060408051838152602081018390527fb463bf17383ba19a0c778fa1f0169c370139c1b955759e8d8287ce70c79cd5899101610a6d565b600061063461063060017fbfaaa2fb63266ff27c2da975f5894955056f50419af651a81f6c5060581857e4611af5565b6000610c58610dfc565b6001600160a01b0316826001600160a01b0316149050919050565b6060806000610c82600061108e565b9050806001600160401b03811115610c9c57610c9c6116ca565b604051908082528060200260200182016040528015610cc5578160200160208202803683370190505b509250806001600160401b03811115610ce057610ce06116ca565b604051908082528060200260200182016040528015610d09578160200160208202803683370190505b50915060005b81811015610d7157600080610d248184610ffd565b91509150818160001b878581518110610d3f57610d3f611b08565b60200260200101878681518110610d5857610d58611b08565b6020908102919091010191909152525050600101610d0f565b50509091565b6000816001600160a01b0316610d8b610dfc565b6001600160a01b0316635aa6e6756040518163ffffffff1660e01b8152600401602060405180830381865afa158015610dc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dec9190611b6f565b6001600160a01b03161492915050565b600061063461063060017f5165972ef41194f06c5007493031d0b927c20741adcb74403b954009fd2c3618611af5565b6000610e3661100c565b604051631430d62960e21b81529091506001600160a01b038316906350c358a490610e659084906004016118aa565b602060405180830381865afa158015610e82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ea69190611ac2565b158015610ebb5750610eb96005826110f1565b155b8015610f3a5750816001600160a01b0316635aa6e6756040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f00573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f249190611b6f565b6001600160a01b0316816001600160a01b031614155b15610f585760405163f20e063760e01b815260040160405180910390fd5b5050565b6000610f688383611106565b9392505050565b6000610f688383611112565b6000808080610f8a868661112f565b909450925050505b9250929050565b604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0090565b610ff1611169565b610ffa81611190565b50565b6000808080610f8a86866112ad565b600061101733610ae8565b15611029575060131936013560601c90565b503390565b6000808484604051602001611044929190611aa9565b60408051601f19818403018152828252805160209182012090830152016040516020818303038152906040528051906020012090506110848684836112d8565b9695505050505050565b6000610500826112ee565b6000610f68836001600160a01b0384166112f9565b6000610f68836001600160a01b0384166113ec565b60606000610f6883611436565b6000610f688383611492565b60006110e98484846114d9565b949350505050565b6000610f68836001600160a01b0384166114f6565b6000610f68838361150e565b60008181526002830160205260408120819055610f68838361151a565b600081815260028301602052604081205481908061115e576111518585611106565b925060009150610f929050565b600192509050610f92565b611171611526565b61118e57604051631afcd79f60e31b815260040160405180910390fd5b565b6001600160a01b0381166111d85760405162461bcd60e51b815260206004820152600f60248201526e2d32b9379031b7b73a3937b63632b960891b60448201526064016109b0565b61120781610a0160017f5165972ef41194f06c5007493031d0b927c20741adcb74403b954009fd2c3618611af5565b61123642610a0160017f6f55f470bdc9cb5f04223fd822021061668e4dccb43e8727b295106dc9769c8b611af5565b61126543610a0160017f812a673dfca07956350df10f8a654925f561d7a0da09bdbe79e653939a14d9f1611af5565b604080516001600160a01b038316815242602082015243918101919091527f1a2dd071001ebf6e03174e3df5b305795a4ad5d41d8fdb9ba41dbbe236713426906060016103cc565b600080806112bb8585611540565b600081815260029690960160205260409095205494959350505050565b6000826112e5858461154c565b14949350505050565b60006105008261158f565b600081815260018301602052604081205480156113e257600061131d600183611af5565b855490915060009061133190600190611af5565b905080821461139657600086600001828154811061135157611351611b08565b906000526020600020015490508087600001848154811061137457611374611b08565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806113a7576113a7611b8c565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610500565b6000915050610500565b60006113f883836114f6565b61142e57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610500565b506000610500565b60608160000180548060200260200160405190810160405280929190818152602001828054801561148657602002820191906000526020600020905b815481526020019060010190808311611472575b50505050509050919050565b6000818152600283016020526040812054801580156114b857506114b68484611106565b155b15610f685760405163015ab34360e11b8152600481018490526024016109b0565b600082815260028401602052604081208290556110e98484611599565b60009081526001919091016020526040902054151590565b6000610f6883836114f6565b6000610f6883836112f9565b6000611530610fc5565b54600160401b900460ff16919050565b6000610f6883836115a5565b600081815b84518110156115875761157d8286838151811061157057611570611b08565b60200260200101516115cf565b9150600101611551565b509392505050565b6000610500825490565b6000610f6883836113ec565b60008260000182815481106115bc576115bc611b08565b9060005260206000200154905092915050565b60008183106115eb576000828152602084905260409020610f68565b6000838152602083905260409020610f68565b60006020828403121561161057600080fd5b5035919050565b6001600160a01b0381168114610ffa57600080fd5b803561163781611617565b919050565b60008060006060848603121561165157600080fd5b833561165c81611617565b9250602084013561166c81611617565b929592945050506040919091013590565b6000806040838503121561169057600080fd5b8235915060208301356116a281611617565b809150509250929050565b6000602082840312156116bf57600080fd5b8135610f6881611617565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715611708576117086116ca565b604052919050565b60006001600160401b03821115611729576117296116ca565b5060051b60200190565b600082601f83011261174457600080fd5b8135602061175961175483611710565b6116e0565b8083825260208201915060208460051b87010193508684111561177b57600080fd5b602086015b848110156117975780358352918301918301611780565b509695505050505050565b600080600080608085870312156117b857600080fd5b84356001600160401b03808211156117cf57600080fd5b818701915087601f8301126117e357600080fd5b813560206117f361175483611710565b82815260059290921b8401810191818101908b84111561181257600080fd5b8286015b8481101561184a5780358681111561182e5760008081fd5b61183c8e86838b0101611733565b845250918301918301611816565b509850508801359250508082111561186157600080fd5b61186d88838901611733565b9450604087013591508082111561188357600080fd5b5061189087828801611733565b92505061189f6060860161162c565b905092959194509250565b6001600160a01b0391909116815260200190565b8015158114610ffa57600080fd5b600080604083850312156118df57600080fd5b82356118ea81611617565b915060208301356116a2816118be565b6020808252825182820181905260009190848201906040850190845b8181101561193b5783516001600160a01b031683529284019291840191600101611916565b50909695505050505050565b6000806000806080858703121561195d57600080fd5b84356001600160401b0381111561197357600080fd5b61197f87828801611733565b945050602085013561199081611617565b93969395505050506040820135916060013590565b600080604083850312156119b857600080fd5b50508035926020909101359150565b60006020808352835180602085015260005b818110156119f5578581018301518582016040015282016119d9565b506000604082860101526040601f19601f8301168501019250505092915050565b60008151808452602080850194506020840160005b83811015611a4757815187529582019590820190600101611a2b565b509495945050505050565b604081526000611a656040830185611a16565b82810360208481019190915284518083528582019282019060005b81811015611a9c57845183529383019391830191600101611a80565b5090979650505050505050565b6001600160a01b03929092168252602082015260400190565b600060208284031215611ad457600080fd5b8151610f68816118be565b634e487b7160e01b600052601160045260246000fd5b8181038181111561050057610500611adf565b634e487b7160e01b600052603260045260246000fd5b606081526000611b316060830186611a16565b8281036020840152611b438186611a16565b91505060018060a01b0383166040830152949350505050565b8082018082111561050057610500611adf565b600060208284031215611b8157600080fd5b8151610f6881611617565b634e487b7160e01b600052603160045260246000fdfe22573091f17911fb166032a3d9e0554aa73d31b7b7ddea4a4dd2995650af84bda2646970667358221220d011b23aa5869c5549188dc384543d96584254ff811eaf2dcd470e210b8b244764736f6c63430008170033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in S
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.