Overview
S Balance
0 S
S Value
$0.00More Info
Private Name Tags
ContractCreator
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Source Code Verified (Exact Match)
Contract Name:
Clans
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 320 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {IBrushToken} from "../interfaces/external/IBrushToken.sol"; import {IPlayers} from "../interfaces/IPlayers.sol"; import {IClans} from "../interfaces/IClans.sol"; import {IBankFactory} from "../interfaces/IBankFactory.sol"; import {IMarketplaceWhitelist} from "../interfaces/external/IMarketplaceWhitelist.sol"; import {IClanMemberLeftCB} from "../interfaces/IClanMemberLeftCB.sol"; import {EstforLibrary} from "../EstforLibrary.sol"; import {BloomFilter} from "../libraries/BloomFilter.sol"; import {ClanRank} from "../globals/clans.sol"; contract Clans is UUPSUpgradeable, OwnableUpgradeable, IClans { using BloomFilter for BloomFilter.Filter; event ClanCreated( uint256 clanId, uint256 playerId, string[] clanInfo, uint256 imageId, uint256 tierId, uint256 createdTimestamp ); event SetClanRank(uint256 clanId, uint256 playerId, ClanRank clan); event InviteSent(uint256 clanId, uint256 playerId, uint256 fromPlayerId); event InvitesSent(uint256 clanId, uint256[] playerIds, uint256 fromPlayerId); event InviteAccepted(uint256 clanId, uint256 playerId); event MemberLeft(uint256 clanId, uint256 playerId, uint256 removedByPlayerId); event JoinRequestSent(uint256 clanId, uint256 playerId); event JoinRequestAccepted(uint256 clanId, uint256 playerId, uint256 acceptedByPlayerId); event JoinRequestsAccepted(uint256 clanId, uint256[] playerIds, uint256 acceptedByPlayerId); event JoinRequestRemoved(uint256 clanId, uint256 playerId); event ClanOwnershipTransferred(uint256 clanId, uint256 playerId); event AddTiers(Tier[] tiers); event EditTiers(Tier[] tiers); event ClanOwnerLeft(uint256 clanId, uint256 playerId); event ClanEdited(uint256 clanId, uint256 playerId, string[] clanInfo, uint256 imageId); event ClanUpgraded(uint256 clanId, uint256 playerId, uint256 tierId); event ClanDestroyed(uint256 clanId); event PlayerRankUpdated(uint256 clanId, uint256 memberId, ClanRank rank, uint256 playerId); event InvitesDeletedByPlayer(uint256[] clanIds, uint256 playerId); event InvitesDeletedByClan(uint256 clanId, uint256[] invitedPlayerIds, uint256 deletedInvitesPlayerId); event JoinRequestsRemovedByClan(uint256 clanId, uint256[] joinRequestPlayerIds, uint256 removingJoinRequestsPlayerId); event EditNameCost(uint256 newCost); event JoinRequestsEnabled(uint256 clanId, bool joinRequestsEnabled, uint256 playerId); event GateKeepNFTs(uint256 clanId, address[] nfts, uint256 playerId); event PinMessage(uint256 clanId, string message, uint256 playerId); event SetInitialMMR(uint256 mmr); event SetBrushDistributionPercentages( uint256 brushBurntPercentage, uint256 brushTreasuryPercentage, uint256 brushDevPercentage ); event AddXP(uint256 clanId, uint256 xp, bool xpEmittedElsewhere); event SetMMR(uint256 clanId, uint256 mmr); // Only used by bridge currently error AlreadyInClan(); error NotOwnerOfPlayer(); error NotOwnerOfPlayerAndActive(); error NotMemberOfClan(); error ClanIsFull(); error OwnerExists(); error InvalidImageId(); error NameTooShort(); error NameTooLong(); error NameInvalidCharacters(); error DiscordTooLong(); error DiscordTooShort(); error DiscordInvalidCharacters(); error TelegramTooLong(); error TelegramInvalidCharacters(); error TwitterTooLong(); error TwitterInvalidCharacters(); error ClanDoesNotExist(); error TierDoesNotExist(); error CannotDowngradeTier(); error TierAlreadyExists(); error NameAlreadyExists(); error ClanDestroyFailedHasMembers(); error PriceTooLow(); error MemberCapacityTooLow(); error BankCapacityTooLow(); error ImageIdTooLow(); error AlreadySentInvite(); error AlreadySentJoinRequest(); error NoJoinRequest(); error RankMustBeLowerRenounce(); error RankNotHighEnough(); error CannotSetSameRank(); error ChangingRankEqualOrHigherThanSelf(); error ChangingRankOfPlayerHigherThanSelf(); error ChangingRankOfPlayerEqualOrHigherThanSelf(); error CannotRenounceToSelf(); error InviteDoesNotExist(); error NoInvitesToDelete(); error NoJoinRequestsToDelete(); error JoinRequestsDisabled(); error TooManyNFTs(); error InvalidNFTType(); error NoGateKeptNFTFound(); error NFTNotWhitelistedOnMarketplace(); error UnsupportedNFTType(); error MessageTooLong(); error NotMMRSetter(); error PercentNotTotal100(); error PlayersAlreadySet(); error BankFactoryAlreadySet(); error ClanNameIsReserved(string name); error NotXPModifier(); error NotBridge(); struct Clan { uint64 ownerPlayerId; uint16 imageId; uint16 memberCount; uint40 createdTimestamp; uint8 tierId; bool disableJoinRequests; uint16 mmr; uint40 xp; string name; mapping(uint256 playerId => bool invited) inviteRequests; NFTInfo[] gateKeptNFTs; } struct PlayerInfo { uint40 clanId; // What clan they are in ClanRank rank; // Current clan rank uint40 requestedClanId; // What clan they have requested to join } struct Tier { uint8 id; uint16 maxMemberCapacity; uint16 maxBankCapacity; uint24 maxImageId; uint40 minimumAge; // How old the clan must be before it can be upgraded to this tier uint80 price; } struct NFTInfo { address nft; uint80 nftType; // e.g erc721 or erc1155 } IBrushToken private _brush; IPlayers private _players; IBankFactory private _bankFactory; IERC1155 private _playerNFT; uint40 private _nextClanId; uint16 private _initialMMR; address private _treasury; uint80 private _editNameCost; address private _dev; uint8 private _brushBurntPercentage; uint8 private _brushTreasuryPercentage; uint8 private _brushDevPercentage; address private _paintswapMarketplaceWhitelist; IClanMemberLeftCB private _territories; IClanMemberLeftCB private _lockedBankVaults; IClanMemberLeftCB private _raids; mapping(uint256 clanId => Clan clan) private _clans; mapping(uint256 playerId => PlayerInfo) private _playerInfo; mapping(uint256 id => Tier tier) private _tiers; mapping(string name => bool exists) private _lowercaseNames; mapping(uint256 clanId => uint40 timestampLeft) private _ownerlessClanTimestamps; // timestamp mapping(address account => bool isModifier) private _xpModifiers; BloomFilter.Filter private _reservedClanNames; // TODO: remove 90 days after launch address private _bridge; // TODO: Bridge Can remove later if no longer need the bridge modifier isOwnerOfPlayer(uint256 playerId) { require(_playerNFT.balanceOf(_msgSender(), playerId) != 0, NotOwnerOfPlayer()); _; } modifier isOwnerOfPlayerAndActive(uint256 playerId) { require(_players.isOwnerOfPlayerAndActive(_msgSender(), playerId), NotOwnerOfPlayerAndActive()); _; } modifier isMinimumRank(uint256 clanId, uint256 playerId, ClanRank rank) { PlayerInfo storage player = _playerInfo[playerId]; require(player.clanId == clanId, NotMemberOfClan()); require(_playerInfo[playerId].rank >= rank, RankNotHighEnough()); _; } modifier isMemberOfClan(uint256 clanId, uint256 playerId) { require(_playerInfo[playerId].clanId == clanId, NotMemberOfClan()); _; } modifier isXPModifier() { require(_xpModifiers[_msgSender()], NotXPModifier()); _; } modifier onlyMMRSetter() { require(_msgSender() == address(_lockedBankVaults), NotMMRSetter()); _; } modifier onlyBridge() { require(_msgSender() == _bridge, NotBridge()); _; } /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } function initialize( IBrushToken brush, IERC1155 playerNFT, address pool, address dev, uint80 editNameCost, address paintswapMarketplaceWhitelist, uint16 initialMMR, uint40 startClanId, address bridge ) external initializer { __Ownable_init(_msgSender()); __UUPSUpgradeable_init(); _brush = brush; _playerNFT = playerNFT; _treasury = pool; _dev = dev; _nextClanId = startClanId; _paintswapMarketplaceWhitelist = paintswapMarketplaceWhitelist; setEditNameCost(editNameCost); setInitialMMR(initialMMR); _reservedClanNames._initialize(4, 420000); _bridge = bridge; } function createClan( uint256 playerId, string calldata name, string calldata discord, string calldata telegram, string calldata twitter, uint16 imageId, uint8 tierId ) external isOwnerOfPlayerAndActive(playerId) { require(!isMemberOfAnyClan(playerId), AlreadyInClan()); Tier storage tier = _tiers[tierId]; _checkTierExists(tierId); _checkClanImage(imageId, tier.maxImageId); uint256 clanId = _nextClanId++; Clan storage clan = _clans[clanId]; clan.ownerPlayerId = uint64(playerId); clan.tierId = tierId; clan.imageId = imageId; clan.memberCount = 1; clan.createdTimestamp = uint40(block.timestamp); clan.mmr = _initialMMR; PlayerInfo storage player = _playerInfo[playerId]; player.clanId = uint32(clanId); player.rank = ClanRank.OWNER; if (player.requestedClanId != 0) { removeJoinRequest(player.requestedClanId, playerId); } (string memory trimmedName, ) = _setName(clanId, name); _checkSocials(discord, telegram, twitter); string[] memory clanInfo = _createClanInfo(trimmedName, discord, telegram, twitter); emit ClanCreated(clanId, playerId, clanInfo, imageId, tierId, block.timestamp); _pay(tier.price); _bankFactory.createBank(_msgSender(), clanId); } function createClanBridge( address from, uint256 playerId, uint256 clanId, string calldata name, string calldata discord, string calldata telegram, string calldata twitter, uint256 imageId, uint256 createdTimestamp, uint256 tierId, uint256 mmr, bool disableJoinRequests ) external onlyBridge { Clan storage clan = _clans[clanId]; clan.ownerPlayerId = uint64(playerId); clan.tierId = uint8(tierId); clan.imageId = uint16(imageId); clan.memberCount = 1; clan.createdTimestamp = uint40(createdTimestamp); clan.mmr = uint16(mmr); PlayerInfo storage player = _playerInfo[playerId]; player.clanId = uint32(clanId); player.rank = ClanRank.OWNER; // don't call _setName to avoid name reservation check clan.name = name; _lowercaseNames[EstforLibrary.toLower(name)] = true; // already trimmed string[] memory clanInfo = _createClanInfo(name, discord, telegram, twitter); emit ClanCreated(clanId, playerId, clanInfo, imageId, tierId, createdTimestamp); if (disableJoinRequests) { clan.disableJoinRequests = disableJoinRequests; emit JoinRequestsEnabled(clanId, !disableJoinRequests, playerId); } emit SetMMR(clanId, mmr); _bankFactory.createBank(from, clanId); } function editClan( uint256 clanId, string calldata name, string calldata discord, string calldata telegram, string calldata twitter, uint256 imageId, uint256 playerId ) external isOwnerOfPlayerAndActive(playerId) isMinimumRank(clanId, playerId, ClanRank.LEADER) { Clan storage clan = _clans[clanId]; Tier storage tier = _tiers[clan.tierId]; _checkClanImage(imageId, tier.maxImageId); (string memory trimmedName, bool nameChanged) = _setName(clanId, name); if (nameChanged) { _pay(_editNameCost); } _checkSocials(discord, telegram, twitter); string[] memory clanInfo = _createClanInfo(trimmedName, discord, telegram, twitter); emit ClanEdited(clanId, playerId, clanInfo, imageId); } function deleteInvitesAsPlayer(uint256[] calldata clanIds, uint256 playerId) external isOwnerOfPlayer(playerId) { require(clanIds.length != 0, NoInvitesToDelete()); for (uint256 i = 0; i < clanIds.length; ++i) { uint256 clanId = clanIds[i]; require(_clans[clanId].inviteRequests[playerId], InviteDoesNotExist()); delete _clans[clanId].inviteRequests[playerId]; } emit InvitesDeletedByPlayer(clanIds, playerId); } function deleteInvitesAsClan( uint256 clanId, uint256[] calldata invitedPlayerIds, uint256 playerId ) external isOwnerOfPlayer(playerId) isMinimumRank(clanId, playerId, ClanRank.SCOUT) { Clan storage clan = _clans[clanId]; require(invitedPlayerIds.length != 0, NoInvitesToDelete()); for (uint256 i = 0; i < invitedPlayerIds.length; ++i) { uint256 invitedPlayerId = invitedPlayerIds[i]; require(clan.inviteRequests[invitedPlayerId], InviteDoesNotExist()); clan.inviteRequests[invitedPlayerId] = false; } emit InvitesDeletedByClan(clanId, invitedPlayerIds, playerId); } function inviteMembers( uint256 clanId, uint256[] calldata memberPlayerIds, uint256 playerId ) external isOwnerOfPlayer(playerId) isMinimumRank(clanId, playerId, ClanRank.SCOUT) { Clan storage clan = _clans[clanId]; Tier storage tier = _tiers[clan.tierId]; require(clan.memberCount + memberPlayerIds.length <= tier.maxMemberCapacity, ClanIsFull()); for (uint256 i = 0; i < memberPlayerIds.length; ++i) { _inviteMember(clanId, memberPlayerIds[i]); } emit InvitesSent(clanId, memberPlayerIds, playerId); } function _acceptInvite(uint256 clanId, uint256 playerId, uint256 gateKeepTokenId) private { Clan storage clan = _clans[clanId]; require(clan.inviteRequests[playerId], InviteDoesNotExist()); require(!isMemberOfAnyClan(playerId), AlreadyInClan()); _checkGateKeeping(clanId, gateKeepTokenId); Tier storage tier = _tiers[clan.tierId]; require(clan.memberCount < tier.maxMemberCapacity, ClanIsFull()); clan.inviteRequests[playerId] = false; clan.memberCount++; PlayerInfo storage player = _playerInfo[playerId]; player.clanId = uint32(clanId); player.rank = ClanRank.COMMONER; player.requestedClanId = 0; emit InviteAccepted(clanId, playerId); } function acceptInvite( uint256 clanId, uint256 playerId, uint256 gateKeepTokenId ) external isOwnerOfPlayerAndActive(playerId) { _acceptInvite(clanId, playerId, gateKeepTokenId); } function requestToJoin( uint256 clanId, uint256 playerId, uint256 gateKeepTokenId ) external isOwnerOfPlayerAndActive(playerId) { _requestToJoin(clanId, playerId, gateKeepTokenId); } function removeJoinRequest(uint256 clanId, uint256 playerId) public isOwnerOfPlayer(playerId) { _playerInfo[playerId].requestedClanId = 0; emit JoinRequestRemoved(clanId, playerId); } function removeJoinRequestsAsClan( uint256 clanId, uint256[] calldata joinRequestPlayerIds, uint256 playerId ) external isOwnerOfPlayer(playerId) isMinimumRank(clanId, playerId, ClanRank.SCOUT) { require(joinRequestPlayerIds.length != 0, NoJoinRequestsToDelete()); for (uint256 i = 0; i < joinRequestPlayerIds.length; ++i) { uint256 joinRequestPlayerId = joinRequestPlayerIds[i]; PlayerInfo storage playerInfo = _playerInfo[joinRequestPlayerId]; require(playerInfo.requestedClanId == clanId, NoJoinRequest()); playerInfo.requestedClanId = 0; } emit JoinRequestsRemovedByClan(clanId, joinRequestPlayerIds, playerId); } function acceptJoinRequests( uint256 clanId, uint256[] calldata newMemberPlayerIds, uint256 playerId ) public isOwnerOfPlayerAndActive(playerId) isMinimumRank(clanId, playerId, ClanRank.SCOUT) { Clan storage clan = _clans[clanId]; Tier storage tier = _tiers[clan.tierId]; require(clan.memberCount + newMemberPlayerIds.length <= tier.maxMemberCapacity, ClanIsFull()); for (uint256 i = 0; i < newMemberPlayerIds.length; ++i) { _acceptJoinRequest(clanId, newMemberPlayerIds[i]); } emit JoinRequestsAccepted(clanId, newMemberPlayerIds, playerId); } function changeRank( uint256 clanId, uint256 memberId, ClanRank rank, uint256 playerId ) public isOwnerOfPlayer(playerId) isMemberOfClan(clanId, memberId) { ClanRank currentMemberRank = _playerInfo[memberId].rank; ClanRank callerRank = _playerInfo[playerId].rank; bool changingSelf = memberId == playerId; require(callerRank > rank, ChangingRankEqualOrHigherThanSelf()); // Cannot change Rank of someone higher or equal yourself if (changingSelf) { require(callerRank >= currentMemberRank, ChangingRankOfPlayerHigherThanSelf()); } else { require(callerRank > currentMemberRank, ChangingRankOfPlayerEqualOrHigherThanSelf()); } require(currentMemberRank != rank, CannotSetSameRank()); bool isDemoting = currentMemberRank > rank; if (isDemoting) { // Are they leaving? if (rank == ClanRank.NONE) { _removeFromClan(clanId, memberId, playerId); } else { // If owner is leaving their post then we need to update the owned state if (currentMemberRank == ClanRank.OWNER) { _ownerCleared(clanId); } _updateRank(clanId, memberId, rank, playerId); } } else { // Promoting _updateRank(clanId, memberId, rank, playerId); } } function changeRanks( uint256 clanId, uint256[] calldata memberIds, ClanRank[] calldata ranks, uint256 playerId ) external isOwnerOfPlayer(playerId) { for (uint256 i = 0; i < memberIds.length; ++i) { changeRank(clanId, memberIds[i], ranks[i], playerId); } } function renounceOwnershipTo( uint256 clanId, uint256 newOwnerPlayerId, ClanRank newRank ) external isOwnerOfPlayer(_clans[clanId].ownerPlayerId) isMemberOfClan(clanId, newOwnerPlayerId) { Clan storage clan = _clans[clanId]; uint256 oldOwnerPlayerId = clan.ownerPlayerId; require(newOwnerPlayerId != oldOwnerPlayerId, CannotRenounceToSelf()); if (newRank != ClanRank.NONE) { require(newRank < ClanRank.OWNER, RankMustBeLowerRenounce()); // Change old owner to new rank _updateRank(clanId, oldOwnerPlayerId, newRank, oldOwnerPlayerId); } else { _removeFromClan(clanId, oldOwnerPlayerId, oldOwnerPlayerId); } _claimOwnership(clanId, newOwnerPlayerId); } // Can claim a clan if there is no owner function claimOwnership( uint256 clanId, uint256 playerId ) external isOwnerOfPlayer(playerId) isMemberOfClan(clanId, playerId) { Clan storage clan = _clans[clanId]; require(clan.ownerPlayerId == 0, OwnerExists()); _claimOwnership(clanId, playerId); } function setJoinRequestsEnabled( uint256 clanId, bool joinRequestsEnabled, uint256 playerId ) external isOwnerOfPlayer(playerId) isMinimumRank(clanId, playerId, ClanRank.SCOUT) { Clan storage clan = _clans[clanId]; clan.disableJoinRequests = !joinRequestsEnabled; emit JoinRequestsEnabled(clanId, joinRequestsEnabled, playerId); } function upgradeClan(uint256 clanId, uint256 playerId, uint8 newTierId) public isOwnerOfPlayer(playerId) { _upgradeClan(clanId, playerId, newTierId); } function pinMessage( uint256 clanId, string calldata message, uint256 playerId ) external isOwnerOfPlayerAndActive(playerId) isMinimumRank(clanId, playerId, ClanRank.LEADER) { require(bytes(message).length <= 200, MessageTooLong()); emit PinMessage(clanId, message, playerId); } function gateKeep( uint256 clanId, NFTInfo[] calldata nftInfos, uint256 playerId ) external isOwnerOfPlayerAndActive(playerId) isMinimumRank(clanId, playerId, ClanRank.LEADER) { require(nftInfos.length <= 5, TooManyNFTs()); address[] memory nfts = new address[](nftInfos.length); IMarketplaceWhitelist paintswapMarketplaceWhitelist = IMarketplaceWhitelist(_paintswapMarketplaceWhitelist); for (uint256 i; i < nftInfos.length; ++i) { // This must be whitelisted by the PaintSwapMarketplace marketplace address nft = nftInfos[i].nft; require(paintswapMarketplaceWhitelist.isWhitelisted(nft), NFTNotWhitelistedOnMarketplace()); // Must be a supported NFT standard uint256 nftType = nftInfos[i].nftType; // Checks supportsInterface is correct if (nftType == 721) { require(IERC721(nft).supportsInterface(type(IERC721).interfaceId), InvalidNFTType()); } else if (nftType == 1155) { require(IERC1155(nft).supportsInterface(type(IERC1155).interfaceId), InvalidNFTType()); } else { revert UnsupportedNFTType(); } nfts[i] = nft; } _clans[clanId].gateKeptNFTs = nftInfos; emit GateKeepNFTs(clanId, nfts, playerId); } // The flag is for cases where XP is added in the future and not part of those events function addXP(uint256 clanId, uint40 xp, bool xpEmittedElsewhere) external isXPModifier { _clans[clanId].xp += xp; emit AddXP(clanId, xp, xpEmittedElsewhere); } function setMMR(uint256 clanId, uint16 mmr) external onlyMMRSetter { _clans[clanId].mmr = mmr; } function _checkClanImage(uint256 imageId, uint256 maxImageId) private pure { require(imageId != 0 && imageId <= maxImageId, InvalidImageId()); } function _setName( uint256 clanId, string calldata name ) private returns (string memory trimmedName, bool nameChanged) { // Trimmed name cannot be empty trimmedName = EstforLibrary.trim(name); require(bytes(trimmedName).length >= 3, NameTooShort()); require(bytes(trimmedName).length <= 20, NameTooLong()); require(EstforLibrary.containsValidNameCharacters(trimmedName), NameInvalidCharacters()); string memory trimmedAndLowercaseName = EstforLibrary.toLower(trimmedName); Clan storage clan = _clans[clanId]; string memory oldName = EstforLibrary.toLower(clan.name); nameChanged = keccak256(abi.encodePacked(oldName)) != keccak256(abi.encodePacked(trimmedAndLowercaseName)); if (nameChanged) { require( !_reservedClanNames._probablyContainsString(trimmedAndLowercaseName), ClanNameIsReserved(trimmedAndLowercaseName) ); require(!_lowercaseNames[trimmedAndLowercaseName], NameAlreadyExists()); if (bytes(oldName).length != 0) { delete _lowercaseNames[oldName]; } _lowercaseNames[trimmedAndLowercaseName] = true; clan.name = trimmedName; } } function _checkSocials(string calldata discord, string calldata telegram, string calldata twitter) private pure { uint256 discordLength = bytes(discord).length; require(discordLength <= 25, DiscordTooLong()); require(discordLength == 0 || discordLength >= 4, DiscordTooShort()); require(EstforLibrary.containsBaselineSocialNameCharacters(discord), DiscordInvalidCharacters()); uint256 telegramLength = bytes(telegram).length; require(telegramLength <= 25, TelegramTooLong()); require(EstforLibrary.containsBaselineSocialNameCharacters(telegram), TelegramInvalidCharacters()); uint256 twitterLength = bytes(twitter).length; require(twitterLength <= 25, TwitterTooLong()); require(EstforLibrary.containsBaselineSocialNameCharacters(twitter), TwitterInvalidCharacters()); } function _createClanInfo( string memory trimmedName, string calldata discord, string calldata telegram, string calldata twitter ) private pure returns (string[] memory clanInfo) { clanInfo = new string[](4); clanInfo[0] = trimmedName; clanInfo[1] = discord; clanInfo[2] = telegram; clanInfo[3] = twitter; } function _checkGateKeeping(uint256 clanId, uint256 gateKeepTokenId) private view { NFTInfo[] memory nftInfo = _clans[clanId].gateKeptNFTs; if (nftInfo.length != 0) { bool foundNFT; // Check the player owns one of these NFTs address sender = _msgSender(); for (uint256 i = 0; i < nftInfo.length; ++i) { if (nftInfo[i].nftType == 1155) { foundNFT = foundNFT || IERC1155(nftInfo[i].nft).balanceOf(sender, gateKeepTokenId) != 0; } else if (nftInfo[i].nftType == 721) { foundNFT = foundNFT || IERC721(nftInfo[i].nft).ownerOf(gateKeepTokenId) == sender; } } require(foundNFT, NoGateKeptNFTFound()); } } function _ownerCleared(uint256 clanId) private { Clan storage clan = _clans[clanId]; uint256 oldOwnerPlayerId = clan.ownerPlayerId; clan.ownerPlayerId = 0; _ownerlessClanTimestamps[clanId] = uint40(block.timestamp); emit ClanOwnerLeft(clanId, oldOwnerPlayerId); } function _updateRank(uint256 clanId, uint256 memberId, ClanRank rank, uint256 playerId) private { PlayerInfo storage player = _playerInfo[memberId]; player.rank = rank; emit PlayerRankUpdated(clanId, memberId, rank, playerId); } function _destroyClan(uint256 clanId) private { // Defensive check Clan storage clan = _clans[clanId]; require(clan.memberCount == 0, ClanDestroyFailedHasMembers()); _lowercaseNames[EstforLibrary.toLower(clan.name)] = false; // Name can be used again delete _clans[clanId]; // Delete the clan emit ClanDestroyed(clanId); } function _removeFromClan(uint256 clanId, uint256 playerId, uint256 removingPlayerId) private { Clan storage clan = _clans[clanId]; if (clan.ownerPlayerId == playerId) { _ownerCleared(clanId); } clan.memberCount--; if (clan.memberCount == 0) { _destroyClan(clanId); } else { emit MemberLeft(clanId, playerId, removingPlayerId); } PlayerInfo storage player = _playerInfo[playerId]; player.clanId = 0; player.rank = ClanRank.NONE; _territories.clanMemberLeft(clanId, playerId); _lockedBankVaults.clanMemberLeft(clanId, playerId); _raids.clanMemberLeft(clanId, playerId); } function _claimOwnership(uint256 clanId, uint256 playerId) private { Clan storage clan = _clans[clanId]; clan.ownerPlayerId = uint64(playerId); delete _ownerlessClanTimestamps[clanId]; _playerInfo[playerId].rank = ClanRank.OWNER; emit ClanOwnershipTransferred(clanId, playerId); } function _pay(uint256 tokenCost) private { IBrushToken brush = _brush; address sender = _msgSender(); brush.burnFrom(sender, (tokenCost * _brushBurntPercentage) / 100); brush.transferFrom(sender, _treasury, (tokenCost * _brushTreasuryPercentage) / 100); brush.transferFrom(sender, _dev, (tokenCost * _brushDevPercentage) / 100); } function _upgradeClan(uint256 clanId, uint256 playerId, uint8 newTierId) private { Tier storage oldTier = _tiers[_clans[clanId].tierId]; require(oldTier.id != 0, ClanDoesNotExist()); require(newTierId > oldTier.id, CannotDowngradeTier()); // require(_playerInfo[playerId].clanId == clanId, NotMemberOfClan()); _checkTierExists(newTierId); Tier storage newTier = _tiers[newTierId]; uint256 priceDifference = newTier.price - oldTier.price; _pay(priceDifference); _clans[clanId].tierId = newTierId; // Increase the tier emit ClanUpgraded(clanId, playerId, newTierId); } function _setTier(Tier calldata tier) private { uint256 tierId = tier.id; // TODO: Some other checks // Price should be higher than the one prior if (tierId > 1) { Tier memory priorTier = _tiers[tierId - 1]; require(tier.price >= priorTier.price, PriceTooLow()); require(tier.maxMemberCapacity >= priorTier.maxMemberCapacity, MemberCapacityTooLow()); require(tier.maxBankCapacity >= priorTier.maxBankCapacity, BankCapacityTooLow()); require(tier.maxImageId >= priorTier.maxImageId, ImageIdTooLow()); } _tiers[tierId] = tier; } function _checkTierExists(uint256 tierId) private view { Tier storage tier = _tiers[tierId]; require(tier.id != 0, TierDoesNotExist()); } function _inviteMember(uint256 clanId, uint256 member) private { Clan storage clan = _clans[clanId]; require(!clan.inviteRequests[member], AlreadySentInvite()); clan.inviteRequests[member] = true; } function _requestToJoin(uint256 clanId, uint256 playerId, uint256 gateKeepTokenId) private { Clan storage clan = _clans[clanId]; require(clan.createdTimestamp != 0, ClanDoesNotExist()); require(!clan.disableJoinRequests, JoinRequestsDisabled()); _checkGateKeeping(clanId, gateKeepTokenId); PlayerInfo storage player = _playerInfo[playerId]; require(!isMemberOfAnyClan(playerId), AlreadyInClan()); uint256 playerRequestedClanId = player.requestedClanId; if (playerRequestedClanId != 0) { require(playerRequestedClanId != clanId, AlreadySentJoinRequest()); emit JoinRequestRemoved(playerRequestedClanId, playerId); } player.requestedClanId = uint32(clanId); emit JoinRequestSent(clanId, playerId); } function _acceptJoinRequest(uint256 clanId, uint256 newMemberPlayerId) private { Clan storage clan = _clans[clanId]; clan.inviteRequests[newMemberPlayerId] = false; clan.memberCount++; PlayerInfo storage player = _playerInfo[newMemberPlayerId]; require(player.requestedClanId == clanId, NoJoinRequest()); player.clanId = uint32(clanId); player.requestedClanId = 0; player.rank = ClanRank.COMMONER; } function getClanIdFromPlayer(uint256 playerId) external view returns (uint256) { return _playerInfo[playerId].clanId; } function getClanNameOfPlayer(uint256 playerId) external view returns (string memory) { uint256 clanId = _playerInfo[playerId].clanId; return _clans[clanId].name; } function canWithdraw(uint256 clanId, uint256 playerId) external view override returns (bool) { return _playerInfo[playerId].clanId == clanId && _playerInfo[playerId].rank >= ClanRank.TREASURER; } function isClanMember(uint256 clanId, uint256 playerId) external view returns (bool) { return _playerInfo[playerId].clanId == clanId; } function isMemberOfAnyClan(uint256 playerId) public view returns (bool) { return _playerInfo[playerId].clanId != 0; } function getClanTierMembership(uint256 playerId) external view returns (uint8) { return _clans[_playerInfo[playerId].clanId].tierId; } function getClanId(uint256 playerId) external view returns (uint256) { return _playerInfo[playerId].clanId; } function getMMR(uint256 clanId) external view returns (uint16 mmr) { mmr = _clans[clanId].mmr; } function hasInviteRequest(uint256 clanId, uint256 playerId) external view returns (bool) { return _clans[clanId].inviteRequests[playerId]; } function maxBankCapacity(uint256 clanId) external view override returns (uint16) { Tier storage tier = _tiers[_clans[clanId].tierId]; return tier.maxBankCapacity; } function maxMemberCapacity(uint256 clanId) external view override returns (uint16) { Tier storage tier = _tiers[_clans[clanId].tierId]; return tier.maxMemberCapacity; } function getRank(uint256 clanId, uint256 playerId) external view returns (ClanRank rank) { if (_playerInfo[playerId].clanId == clanId) { return _playerInfo[playerId].rank; } return ClanRank.NONE; } function getEditNameCost() external view returns (uint80) { return _editNameCost; } function getPlayerInfo(uint256 playerId) external view returns (PlayerInfo memory) { return _playerInfo[playerId]; } function getLowercaseNames(string calldata name) external view returns (bool) { return _lowercaseNames[name]; } function getTier(uint256 tierId) external view returns (Tier memory) { return _tiers[tierId]; } function getClan( uint256 clanId ) external view returns ( uint64 ownerPlayerId, uint16 imageId, uint16 memberCount, uint40 createdTimestamp, uint8 tierId, bool disableJoinRequests, uint16 mmr, string memory name, NFTInfo[] memory gateKeptNFTs ) { Clan storage clan = _clans[clanId]; return ( clan.ownerPlayerId, clan.imageId, clan.memberCount, clan.createdTimestamp, clan.tierId, clan.disableJoinRequests, clan.mmr, clan.name, clan.gateKeptNFTs ); } function isClanNameReserved(string calldata clanName) public view returns (bool) { return _reservedClanNames._probablyContainsString(EstforLibrary.toLower(clanName)); } function addTiers(Tier[] calldata tiers) external onlyOwner { for (uint256 i; i < tiers.length; ++i) { require(tiers[i].id != 0 && _tiers[tiers[i].id].id == 0, TierAlreadyExists()); _setTier(tiers[i]); } emit AddTiers(tiers); } function editTiers(Tier[] calldata tiers) external onlyOwner { for (uint256 i; i < tiers.length; ++i) { _checkTierExists(tiers[i].id); _setTier(tiers[i]); } emit EditTiers(tiers); } function initializeAddresses( IPlayers players, IBankFactory bankFactory, IClanMemberLeftCB territories, IClanMemberLeftCB lockedBankVaults, IClanMemberLeftCB raids ) external onlyOwner { require(address(_bankFactory) == address(0) || _bankFactory == bankFactory, BankFactoryAlreadySet()); _players = players; _bankFactory = bankFactory; _territories = territories; _lockedBankVaults = lockedBankVaults; _raids = raids; } function setXPModifiers(address[] calldata accounts, bool isModifier) external onlyOwner { for (uint256 i; i < accounts.length; ++i) { _xpModifiers[accounts[i]] = isModifier; } } function setEditNameCost(uint80 editNameCost) public onlyOwner { _editNameCost = editNameCost; emit EditNameCost(editNameCost); } function setInitialMMR(uint16 mmr) public onlyOwner { _initialMMR = mmr; emit SetInitialMMR(mmr); } function setBrushDistributionPercentages( uint8 brushBurntPercentage, uint8 brushTreasuryPercentage, uint8 brushDevPercentage ) external onlyOwner { require(brushBurntPercentage + brushTreasuryPercentage + brushDevPercentage == 100, PercentNotTotal100()); _brushBurntPercentage = brushBurntPercentage; _brushTreasuryPercentage = brushTreasuryPercentage; _brushDevPercentage = brushDevPercentage; emit SetBrushDistributionPercentages(brushBurntPercentage, brushTreasuryPercentage, brushDevPercentage); } function setReservedNameBits(uint256[] calldata positions) external onlyOwner { _reservedClanNames._addPositions(positions); } function addReservedClanNames(string[] calldata names) external onlyOwner { for (uint256 i = 0; i < names.length; ++i) { _reservedClanNames._addString(EstforLibrary.toLower(names[i])); } } // solhint-disable-next-line no-empty-blocks function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) pragma solidity ^0.8.20; import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol"; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * The initial owner is set to the address provided by the deployer. This can * later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { /// @custom:storage-location erc7201:openzeppelin.storage.Ownable struct OwnableStorage { address _owner; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300; function _getOwnableStorage() private pure returns (OwnableStorage storage $) { assembly ("memory-safe") { $.slot := OwnableStorageLocation } } /** * @dev The caller account is not authorized to perform an operation. */ error OwnableUnauthorizedAccount(address account); /** * @dev The owner is not a valid owner account. (eg. `address(0)`) */ error OwnableInvalidOwner(address owner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the address provided by the deployer as the initial owner. */ function __Ownable_init(address initialOwner) internal onlyInitializing { __Ownable_init_unchained(initialOwner); } function __Ownable_init_unchained(address initialOwner) internal onlyInitializing { if (initialOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(initialOwner); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { OwnableStorage storage $ = _getOwnableStorage(); return $._owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { if (owner() != _msgSender()) { revert OwnableUnauthorizedAccount(_msgSender()); } } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { if (newOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { OwnableStorage storage $ = _getOwnableStorage(); address oldOwner = $._owner; $._owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// 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 ("memory-safe") { $.slot := INITIALIZABLE_STORAGE } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (proxy/utils/UUPSUpgradeable.sol) pragma solidity ^0.8.20; import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol"; import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; import {Initializable} from "./Initializable.sol"; /** * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy. * * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing * `UUPSUpgradeable` with a custom implementation of upgrades. * * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism. */ abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable { /// @custom:oz-upgrades-unsafe-allow state-variable-immutable address private immutable __self = address(this); /** * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)` * and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called, * while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string. * If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function * during an upgrade. */ string public constant UPGRADE_INTERFACE_VERSION = "5.0.0"; /** * @dev The call is from an unauthorized context. */ error UUPSUnauthorizedCallContext(); /** * @dev The storage `slot` is unsupported as a UUID. */ error UUPSUnsupportedProxiableUUID(bytes32 slot); /** * @dev Check that the execution is being performed through a delegatecall call and that the execution context is * a proxy contract with an implementation (as defined in ERC-1967) pointing to self. This should only be the case * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a * function through ERC-1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to * fail. */ modifier onlyProxy() { _checkProxy(); _; } /** * @dev Check that the execution is not being performed through a delegate call. This allows a function to be * callable on the implementing contract but not through proxies. */ modifier notDelegated() { _checkNotDelegated(); _; } function __UUPSUpgradeable_init() internal onlyInitializing { } function __UUPSUpgradeable_init_unchained() internal onlyInitializing { } /** * @dev Implementation of the ERC-1822 {proxiableUUID} function. This returns the storage slot used by the * implementation. It is used to validate the implementation's compatibility when performing an upgrade. * * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier. */ function proxiableUUID() external view virtual notDelegated returns (bytes32) { return ERC1967Utils.IMPLEMENTATION_SLOT; } /** * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call * encoded in `data`. * * Calls {_authorizeUpgrade}. * * Emits an {Upgraded} event. * * @custom:oz-upgrades-unsafe-allow-reachable delegatecall */ function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy { _authorizeUpgrade(newImplementation); _upgradeToAndCallUUPS(newImplementation, data); } /** * @dev Reverts if the execution is not performed via delegatecall or the execution * context is not of a proxy with an ERC-1967 compliant implementation pointing to self. * See {_onlyProxy}. */ function _checkProxy() internal view virtual { if ( address(this) == __self || // Must be called through delegatecall ERC1967Utils.getImplementation() != __self // Must be called through an active proxy ) { revert UUPSUnauthorizedCallContext(); } } /** * @dev Reverts if the execution is performed via delegatecall. * See {notDelegated}. */ function _checkNotDelegated() internal view virtual { if (address(this) != __self) { // Must not be called through delegatecall revert UUPSUnauthorizedCallContext(); } } /** * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by * {upgradeToAndCall}. * * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}. * * ```solidity * function _authorizeUpgrade(address) internal onlyOwner {} * ``` */ function _authorizeUpgrade(address newImplementation) internal virtual; /** * @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call. * * As a security check, {proxiableUUID} is invoked in the new implementation, and the return value * is expected to be the implementation slot in ERC-1967. * * Emits an {IERC1967-Upgraded} event. */ function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private { try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) { if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) { revert UUPSUnsupportedProxiableUUID(slot); } ERC1967Utils.upgradeToAndCall(newImplementation, data); } catch { // The implementation is not UUPS revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) pragma solidity ^0.8.20; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC1822.sol) pragma solidity ^0.8.20; /** * @dev ERC-1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified * proxy whose upgrades are fully controlled by the current implementation. */ interface IERC1822Proxiable { /** * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation * address. * * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this * function revert if invoked through a proxy. */ function proxiableUUID() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1967.sol) pragma solidity ^0.8.20; /** * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC. */ interface IERC1967 { /** * @dev Emitted when the implementation is upgraded. */ event Upgraded(address indexed implementation); /** * @dev Emitted when the admin account has changed. */ event AdminChanged(address previousAdmin, address newAdmin); /** * @dev Emitted when the beacon is changed. */ event BeaconUpgraded(address indexed beacon); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol) pragma solidity ^0.8.20; /** * @dev This is the interface that {BeaconProxy} expects of its beacon. */ interface IBeacon { /** * @dev Must return an address that can be used as a delegate call target. * * {UpgradeableBeacon} will check that this address is a contract. */ function implementation() external view returns (address); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (proxy/ERC1967/ERC1967Utils.sol) pragma solidity ^0.8.21; import {IBeacon} from "../beacon/IBeacon.sol"; import {IERC1967} from "../../interfaces/IERC1967.sol"; import {Address} from "../../utils/Address.sol"; import {StorageSlot} from "../../utils/StorageSlot.sol"; /** * @dev This library provides getters and event emitting update functions for * https://eips.ethereum.org/EIPS/eip-1967[ERC-1967] slots. */ library ERC1967Utils { /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1. */ // solhint-disable-next-line private-vars-leading-underscore bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @dev The `implementation` of the proxy is invalid. */ error ERC1967InvalidImplementation(address implementation); /** * @dev The `admin` of the proxy is invalid. */ error ERC1967InvalidAdmin(address admin); /** * @dev The `beacon` of the proxy is invalid. */ error ERC1967InvalidBeacon(address beacon); /** * @dev An upgrade function sees `msg.value > 0` that may be lost. */ error ERC1967NonPayable(); /** * @dev Returns the current implementation address. */ function getImplementation() internal view returns (address) { return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value; } /** * @dev Stores a new address in the ERC-1967 implementation slot. */ function _setImplementation(address newImplementation) private { if (newImplementation.code.length == 0) { revert ERC1967InvalidImplementation(newImplementation); } StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation; } /** * @dev Performs implementation upgrade with additional setup call if data is nonempty. * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected * to avoid stuck value in the contract. * * Emits an {IERC1967-Upgraded} event. */ function upgradeToAndCall(address newImplementation, bytes memory data) internal { _setImplementation(newImplementation); emit IERC1967.Upgraded(newImplementation); if (data.length > 0) { Address.functionDelegateCall(newImplementation, data); } else { _checkNonPayable(); } } /** * @dev Storage slot with the admin of the contract. * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1. */ // solhint-disable-next-line private-vars-leading-underscore bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; /** * @dev Returns the current admin. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by ERC-1967) using * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` */ function getAdmin() internal view returns (address) { return StorageSlot.getAddressSlot(ADMIN_SLOT).value; } /** * @dev Stores a new address in the ERC-1967 admin slot. */ function _setAdmin(address newAdmin) private { if (newAdmin == address(0)) { revert ERC1967InvalidAdmin(address(0)); } StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin; } /** * @dev Changes the admin of the proxy. * * Emits an {IERC1967-AdminChanged} event. */ function changeAdmin(address newAdmin) internal { emit IERC1967.AdminChanged(getAdmin(), newAdmin); _setAdmin(newAdmin); } /** * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. * This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1. */ // solhint-disable-next-line private-vars-leading-underscore bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; /** * @dev Returns the current beacon. */ function getBeacon() internal view returns (address) { return StorageSlot.getAddressSlot(BEACON_SLOT).value; } /** * @dev Stores a new beacon in the ERC-1967 beacon slot. */ function _setBeacon(address newBeacon) private { if (newBeacon.code.length == 0) { revert ERC1967InvalidBeacon(newBeacon); } StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon; address beaconImplementation = IBeacon(newBeacon).implementation(); if (beaconImplementation.code.length == 0) { revert ERC1967InvalidImplementation(beaconImplementation); } } /** * @dev Change the beacon and trigger a setup call if data is nonempty. * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected * to avoid stuck value in the contract. * * Emits an {IERC1967-BeaconUpgraded} event. * * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for * efficiency. */ function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal { _setBeacon(newBeacon); emit IERC1967.BeaconUpgraded(newBeacon); if (data.length > 0) { Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data); } else { _checkNonPayable(); } } /** * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract * if an upgrade doesn't perform an initialization call. */ function _checkNonPayable() private { if (msg.value > 0) { revert ERC1967NonPayable(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC1155/IERC1155.sol) pragma solidity ^0.8.20; import {IERC165} from "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC-1155 compliant contract, as defined in the * https://eips.ethereum.org/EIPS/eip-1155[ERC]. */ interface IERC1155 is IERC165 { /** * @dev Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`. */ event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value); /** * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all * transfers. */ event TransferBatch( address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values ); /** * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to * `approved`. */ event ApprovalForAll(address indexed account, address indexed operator, bool approved); /** * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI. * * If an {URI} event was emitted for `id`, the standard * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value * returned by {IERC1155MetadataURI-uri}. */ event URI(string value, uint256 indexed id); /** * @dev Returns the value of tokens of token type `id` owned by `account`. */ function balanceOf(address account, uint256 id) external view returns (uint256); /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}. * * Requirements: * * - `accounts` and `ids` must have the same length. */ function balanceOfBatch( address[] calldata accounts, uint256[] calldata ids ) external view returns (uint256[] memory); /** * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`, * * Emits an {ApprovalForAll} event. * * Requirements: * * - `operator` cannot be the zero address. */ function setApprovalForAll(address operator, bool approved) external; /** * @dev Returns true if `operator` is approved to transfer ``account``'s tokens. * * See {setApprovalForAll}. */ function isApprovedForAll(address account, address operator) external view returns (bool); /** * @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`. * * WARNING: This function can potentially allow a reentrancy attack when transferring tokens * to an untrusted contract, when invoking {onERC1155Received} on the receiver. * Ensure to follow the checks-effects-interactions pattern and consider employing * reentrancy guards when interacting with untrusted contracts. * * Emits a {TransferSingle} event. * * Requirements: * * - `to` cannot be the zero address. * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}. * - `from` must have a balance of tokens of type `id` of at least `value` amount. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the * acceptance magic value. */ function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes calldata data) external; /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}. * * WARNING: This function can potentially allow a reentrancy attack when transferring tokens * to an untrusted contract, when invoking {onERC1155BatchReceived} on the receiver. * Ensure to follow the checks-effects-interactions pattern and consider employing * reentrancy guards when interacting with untrusted contracts. * * Emits either a {TransferSingle} or a {TransferBatch} event, depending on the length of the array arguments. * * Requirements: * * - `ids` and `values` must have the same length. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the * acceptance magic value. */ function safeBatchTransferFrom( address from, address to, uint256[] calldata ids, uint256[] calldata values, bytes calldata data ) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-20 standard as defined in the ERC. */ interface IERC20 { /** * @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); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) 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 a `value` amount of tokens 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 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.20; import {IERC165} from "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC-721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon * a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC-721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or * {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon * a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721 * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must * understand this adds an external call which potentially creates a reentrancy vulnerability. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 tokenId) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the address zero. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool approved) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/Address.sol) pragma solidity ^0.8.20; import {Errors} from "./Errors.sol"; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert Errors.InsufficientBalance(address(this).balance, amount); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert Errors.FailedCall(); } } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {Errors.FailedCall} error. * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert Errors.InsufficientBalance(address(this).balance, value); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case * of an unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {Errors.FailedCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}. */ function _revert(bytes memory returndata) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly ("memory-safe") { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert Errors.FailedCall(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol) pragma solidity ^0.8.20; /** * @dev Collection of common custom errors used in multiple contracts * * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library. * It is recommended to avoid relying on the error API for critical functionality. * * _Available since v5.1._ */ library Errors { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error InsufficientBalance(uint256 balance, uint256 needed); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedCall(); /** * @dev The deployment failed. */ error FailedDeployment(); /** * @dev A necessary precompile is missing. */ error MissingPrecompile(address); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[ERC]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol) // This file was procedurally generated from scripts/generate/templates/StorageSlot.js. pragma solidity ^0.8.20; /** * @dev Library for reading and writing primitive types to specific storage slots. * * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. * This library helps with reading and writing to such slots without the need for inline assembly. * * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. * * Example usage to set ERC-1967 implementation slot: * ```solidity * contract ERC1967 { * // Define the slot. Alternatively, use the SlotDerivation library to derive the slot. * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; * * function _getImplementation() internal view returns (address) { * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; * } * * function _setImplementation(address newImplementation) internal { * require(newImplementation.code.length > 0); * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; * } * } * ``` * * TIP: Consider using this library along with {SlotDerivation}. */ library StorageSlot { struct AddressSlot { address value; } struct BooleanSlot { bool value; } struct Bytes32Slot { bytes32 value; } struct Uint256Slot { uint256 value; } struct Int256Slot { int256 value; } struct StringSlot { string value; } struct BytesSlot { bytes value; } /** * @dev Returns an `AddressSlot` with member `value` located at `slot`. */ function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `BooleanSlot` with member `value` located at `slot`. */ function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `Bytes32Slot` with member `value` located at `slot`. */ function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `Uint256Slot` with member `value` located at `slot`. */ function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `Int256Slot` with member `value` located at `slot`. */ function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `StringSlot` with member `value` located at `slot`. */ function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns an `StringSlot` representation of the string storage pointer `store`. */ function getStringSlot(string storage store) internal pure returns (StringSlot storage r) { assembly ("memory-safe") { r.slot := store.slot } } /** * @dev Returns a `BytesSlot` with member `value` located at `slot`. */ function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`. */ function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) { assembly ("memory-safe") { r.slot := store.slot } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/BitMaps.sol) pragma solidity ^0.8.20; /** * @dev Library for managing uint256 to bool mapping in a compact and efficient way, provided the keys are sequential. * Largely inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor]. * * BitMaps pack 256 booleans across each bit of a single 256-bit slot of `uint256` type. * Hence booleans corresponding to 256 _sequential_ indices would only consume a single slot, * unlike the regular `bool` which would consume an entire slot for a single value. * * This results in gas savings in two ways: * * - Setting a zero value to non-zero only once every 256 times * - Accessing the same warm slot for every 256 _sequential_ indices */ library BitMaps { struct BitMap { mapping(uint256 bucket => uint256) _data; } /** * @dev Returns whether the bit at `index` is set. */ function get(BitMap storage bitmap, uint256 index) internal view returns (bool) { uint256 bucket = index >> 8; uint256 mask = 1 << (index & 0xff); return bitmap._data[bucket] & mask != 0; } /** * @dev Sets the bit at `index` to the boolean `value`. */ function setTo(BitMap storage bitmap, uint256 index, bool value) internal { if (value) { set(bitmap, index); } else { unset(bitmap, index); } } /** * @dev Sets the bit at `index`. */ function set(BitMap storage bitmap, uint256 index) internal { uint256 bucket = index >> 8; uint256 mask = 1 << (index & 0xff); bitmap._data[bucket] |= mask; } /** * @dev Unsets the bit at `index`. */ function unset(BitMap storage bitmap, uint256 index) internal { uint256 bucket = index >> 8; uint256 mask = 1 << (index & 0xff); bitmap._data[bucket] &= ~mask; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {IPlayers} from "./interfaces/IPlayers.sol"; // solhint-disable-next-line no-global-import import "./globals/all.sol"; // This file contains methods for interacting with generic functions like trimming strings, lowercase etc. // Also has some shared functions for rewards library EstforLibrary { error RandomRewardsMustBeInOrder(uint16 chance1, uint16 chance2); error RandomRewardNoDuplicates(); error GuaranteedRewardsNoDuplicates(); error TooManyGuaranteedRewards(); error TooManyRandomRewards(); function isWhitespace(bytes1 _char) internal pure returns (bool) { return _char == 0x20 || // Space _char == 0x09 || // Tab _char == 0x0a || // Line feed _char == 0x0D || // Carriage return _char == 0x0B || // Vertical tab _char == 0x00; // empty byte } function leftTrim(string memory str) internal pure returns (string memory) { bytes memory b = bytes(str); uint256 strLen = b.length; uint256 start = type(uint256).max; // Find the index of the first non-whitespace character for (uint256 i = 0; i < strLen; ++i) { bytes1 char = b[i]; if (!isWhitespace(char)) { start = i; break; } } if (start == type(uint256).max) { return ""; } // Copy the remainder to a new string bytes memory trimmedBytes = new bytes(strLen - start); for (uint256 i = start; i < strLen; ++i) { trimmedBytes[i - start] = b[i]; } return string(trimmedBytes); } function rightTrim(string calldata str) internal pure returns (string memory) { bytes memory b = bytes(str); uint256 strLen = b.length; if (strLen == 0) { return ""; } int end = -1; // Find the index of the last non-whitespace character for (int i = int(strLen) - 1; i >= 0; --i) { bytes1 char = b[uint256(i)]; if (!isWhitespace(char)) { end = i; break; } } if (end == -1) { return ""; } bytes memory trimmedBytes = new bytes(uint256(end) + 1); for (uint256 i = 0; i <= uint256(end); ++i) { trimmedBytes[i] = b[i]; } return string(trimmedBytes); } function trim(string calldata str) external pure returns (string memory) { return leftTrim(rightTrim(str)); } // Assumes the string is already trimmed function containsValidNameCharacters(string calldata name) external pure returns (bool) { bytes memory b = bytes(name); bool lastCharIsWhitespace; for (uint256 i = 0; i < b.length; ++i) { bytes1 char = b[i]; bool isUpperCaseLetter = (char >= 0x41) && (char <= 0x5A); // A-Z bool isLowerCaseLetter = (char >= 0x61) && (char <= 0x7A); // a-z bool isDigit = (char >= 0x30) && (char <= 0x39); // 0-9 bool isSpecialCharacter = (char == 0x2D) || (char == 0x5F) || (char == 0x2E) || (char == 0x20); // "-", "_", ".", and " " bool _isWhitespace = isWhitespace(char); bool hasMultipleWhitespaceInRow = lastCharIsWhitespace && _isWhitespace; lastCharIsWhitespace = _isWhitespace; if ((!isUpperCaseLetter && !isLowerCaseLetter && !isDigit && !isSpecialCharacter) || hasMultipleWhitespaceInRow) { return false; } } return true; } function containsValidDiscordCharacters(string calldata discord) external pure returns (bool) { bytes memory discordBytes = bytes(discord); for (uint256 i = 0; i < discordBytes.length; ++i) { bytes1 char = discordBytes[i]; bool isUpperCaseLetter = (char >= 0x41) && (char <= 0x5A); // A-Z bool isLowerCaseLetter = (char >= 0x61) && (char <= 0x7A); // a-z bool isDigit = (char >= 0x30) && (char <= 0x39); // 0-9 if (!isUpperCaseLetter && !isLowerCaseLetter && !isDigit) { return false; } } return true; } function containsValidTelegramCharacters(string calldata telegram) external pure returns (bool) { bytes memory telegramBytes = bytes(telegram); for (uint256 i = 0; i < telegramBytes.length; ++i) { bytes1 char = telegramBytes[i]; bool isUpperCaseLetter = (char >= 0x41) && (char <= 0x5A); // A-Z bool isLowerCaseLetter = (char >= 0x61) && (char <= 0x7A); // a-z bool isDigit = (char >= 0x30) && (char <= 0x39); // 0-9 bool isPlus = char == 0x2B; // "+" if (!isUpperCaseLetter && !isLowerCaseLetter && !isDigit && !isPlus) { return false; } } return true; } function containsValidTwitterCharacters(string calldata twitter) external pure returns (bool) { bytes memory twitterBytes = bytes(twitter); for (uint256 i = 0; i < twitterBytes.length; ++i) { bytes1 char = twitterBytes[i]; bool isUpperCaseLetter = (char >= 0x41) && (char <= 0x5A); // A-Z bool isLowerCaseLetter = (char >= 0x61) && (char <= 0x7A); // a-z bool isDigit = (char >= 0x30) && (char <= 0x39); // 0-9 if (!isUpperCaseLetter && !isLowerCaseLetter && !isDigit) { return false; } } return true; } function containsBaselineSocialNameCharacters(string calldata socialMediaName) external pure returns (bool) { bytes memory socialMediaNameBytes = bytes(socialMediaName); for (uint256 i = 0; i < socialMediaNameBytes.length; ++i) { bytes1 char = socialMediaNameBytes[i]; bool isUpperCaseLetter = (char >= 0x41) && (char <= 0x5A); // A-Z bool isLowerCaseLetter = (char >= 0x61) && (char <= 0x7A); // a-z bool isDigit = (char >= 0x30) && (char <= 0x39); // 0-9 bool isUnderscore = char == 0x5F; // "_" bool isPeriod = char == 0x2E; // "." bool isPlus = char == 0x2B; // "+" if (!isUpperCaseLetter && !isLowerCaseLetter && !isDigit && !isUnderscore && !isPeriod && !isPlus) { return false; } } return true; } function toLower(string memory str) internal pure returns (string memory) { bytes memory lowerStr = abi.encodePacked(str); for (uint256 i = 0; i < lowerStr.length; ++i) { bytes1 char = lowerStr[i]; if ((char >= 0x41) && (char <= 0x5A)) { // So we add 32 to make it lowercase lowerStr[i] = bytes1(uint8(char) + 32); } } return string(lowerStr); } // This should match the one below, useful when a calldata array is needed and for external testing function _binarySearchMemory(uint64[] calldata array, uint256 target) internal pure returns (uint256) { uint256 low = 0; uint256 high = array.length - 1; while (low <= high) { uint256 mid = low + (high - low) / 2; if (array[mid] == target) { return mid; // Element found } else if (array[mid] < target) { low = mid + 1; } else { // Check to prevent underflow if (mid != 0) { high = mid - 1; } else { // If mid is 0 and _arr[mid] is not the target, the element is not in the array break; } } } return type(uint256).max; // Element not found } function binarySearchMemory(uint64[] calldata array, uint256 target) external pure returns (uint256) { return _binarySearchMemory(array, target); } // This should match the one above function _binarySearch(uint64[] storage array, uint256 target) internal view returns (uint256) { uint256 low = 0; uint256 high = array.length - 1; while (low <= high) { uint256 mid = low + (high - low) / 2; if (array[mid] == target) { return mid; // Element found } else if (array[mid] < target) { low = mid + 1; } else { // Check to prevent underflow if (mid != 0) { high = mid - 1; } else { // If mid is 0 and _arr[mid] is not the target, the element is not in the array break; } } } return type(uint256).max; // Element not found } function binarySearch(uint64[] storage array, uint256 target) external view returns (uint256) { return _binarySearch(array, target); } function _shuffleArray(uint64[] memory array, uint256 randomNumber) internal pure returns (uint64[] memory output) { for (uint256 i; i < array.length; ++i) { uint256 n = i + (randomNumber % (array.length - i)); if (i != n) { uint64 temp = array[n]; array[n] = array[i]; array[i] = temp; } } return array; } function _getRandomInRange16( uint256 randomWord, uint256 shift, int16 minValue, int16 maxValue ) internal pure returns (int16) { return int16(minValue + (int16(int256((randomWord >> shift) & 0xFFFF) % (maxValue - minValue + 1)))); } function _getRandomFromArray16( uint256 randomWord, uint256 shift, uint16[] storage arr, uint256 arrLength ) internal view returns (uint16) { return arr[_getRandomIndexFromArray16(randomWord, shift, arrLength)]; } function _getRandomFrom3ElementArray16( uint256 randomWord, uint256 shift, uint16[3] memory arr ) internal pure returns (uint16) { return arr[_getRandomIndexFromArray16(randomWord, shift, arr.length)]; } function _getRandomIndexFromArray16( uint256 randomWord, uint256 shift, uint256 arrLength ) internal pure returns (uint16) { return uint16(((randomWord >> shift) & 0xFFFF) % arrLength); } function setActionGuaranteedRewards( GuaranteedReward[] calldata guaranteedRewards, ActionRewards storage actionRewards ) external { _setActionGuaranteedRewards(guaranteedRewards, actionRewards); } function setActionRandomRewards(RandomReward[] calldata randomRewards, ActionRewards storage actionRewards) external { _setActionRandomRewards(randomRewards, actionRewards); } function _setActionGuaranteedRewards( GuaranteedReward[] calldata guaranteedRewards, ActionRewards storage actionRewards ) internal { uint256 guaranteedRewardsLength = guaranteedRewards.length; if (guaranteedRewardsLength != 0) { actionRewards.guaranteedRewardTokenId1 = guaranteedRewards[0].itemTokenId; actionRewards.guaranteedRewardRate1 = guaranteedRewards[0].rate; } if (guaranteedRewardsLength > 1) { actionRewards.guaranteedRewardTokenId2 = guaranteedRewards[1].itemTokenId; actionRewards.guaranteedRewardRate2 = guaranteedRewards[1].rate; require( actionRewards.guaranteedRewardTokenId1 != actionRewards.guaranteedRewardTokenId2, GuaranteedRewardsNoDuplicates() ); } if (guaranteedRewardsLength > 2) { actionRewards.guaranteedRewardTokenId3 = guaranteedRewards[2].itemTokenId; actionRewards.guaranteedRewardRate3 = guaranteedRewards[2].rate; uint256 bounds = guaranteedRewardsLength - 1; for (uint256 i; i < bounds; ++i) { require( guaranteedRewards[i].itemTokenId != guaranteedRewards[guaranteedRewardsLength - 1].itemTokenId, GuaranteedRewardsNoDuplicates() ); } } require(guaranteedRewardsLength <= 3, TooManyGuaranteedRewards()); } // Random rewards have most common one first function _setActionRandomRewards( RandomReward[] calldata randomRewards, ActionRewards storage actionRewards ) internal { uint256 randomRewardsLength = randomRewards.length; if (randomRewardsLength != 0) { actionRewards.randomRewardTokenId1 = randomRewards[0].itemTokenId; actionRewards.randomRewardChance1 = randomRewards[0].chance; actionRewards.randomRewardAmount1 = randomRewards[0].amount; } if (randomRewardsLength > 1) { actionRewards.randomRewardTokenId2 = randomRewards[1].itemTokenId; actionRewards.randomRewardChance2 = randomRewards[1].chance; actionRewards.randomRewardAmount2 = randomRewards[1].amount; require( actionRewards.randomRewardChance2 <= actionRewards.randomRewardChance1, RandomRewardsMustBeInOrder(randomRewards[0].chance, randomRewards[1].chance) ); require(actionRewards.randomRewardTokenId1 != actionRewards.randomRewardTokenId2, RandomRewardNoDuplicates()); } if (randomRewardsLength > 2) { actionRewards.randomRewardTokenId3 = randomRewards[2].itemTokenId; actionRewards.randomRewardChance3 = randomRewards[2].chance; actionRewards.randomRewardAmount3 = randomRewards[2].amount; require( actionRewards.randomRewardChance3 <= actionRewards.randomRewardChance2, RandomRewardsMustBeInOrder(randomRewards[1].chance, randomRewards[2].chance) ); uint256 bounds = randomRewardsLength - 1; for (uint256 i; i < bounds; ++i) { require( randomRewards[i].itemTokenId != randomRewards[randomRewardsLength - 1].itemTokenId, RandomRewardNoDuplicates() ); } } if (randomRewards.length > 3) { actionRewards.randomRewardTokenId4 = randomRewards[3].itemTokenId; actionRewards.randomRewardChance4 = randomRewards[3].chance; actionRewards.randomRewardAmount4 = randomRewards[3].amount; require( actionRewards.randomRewardChance4 <= actionRewards.randomRewardChance3, RandomRewardsMustBeInOrder(randomRewards[2].chance, randomRewards[3].chance) ); uint256 bounds = randomRewards.length - 1; for (uint256 i; i < bounds; ++i) { require( randomRewards[i].itemTokenId != randomRewards[randomRewards.length - 1].itemTokenId, RandomRewardNoDuplicates() ); } } require(randomRewards.length <= 4, TooManyRandomRewards()); } function _get16bitSlice(bytes memory b, uint256 index) internal pure returns (uint16) { uint256 key = index * 2; return uint16(b[key] | (bytes2(b[key + 1]) >> 8)); } // Helper function to get random value between min and max (inclusive) for uint8 function _getRandomInRange8(uint8 minValue, uint8 maxValue, uint8 randomness) internal pure returns (uint8) { if (maxValue <= minValue) { return minValue; } uint8 range = maxValue - minValue + 1; // Use modulo to get value in range and add minValue return uint8((uint16(randomness) % uint16(range)) + uint16(minValue)); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {Skill, Attire, CombatStyle, CombatStats} from "./misc.sol"; import {GuaranteedReward, RandomReward} from "./rewards.sol"; enum ActionQueueStrategy { OVERWRITE, APPEND, KEEP_LAST_IN_PROGRESS } struct QueuedActionInput { Attire attire; uint16 actionId; uint16 regenerateId; // Food (combat), maybe something for non-combat later uint16 choiceId; // Melee/Ranged/Magic (combat), logs, ore (non-combat) uint16 rightHandEquipmentTokenId; // Axe/Sword/bow, can be empty uint16 leftHandEquipmentTokenId; // Shield, can be empty uint24 timespan; // How long to queue the action for uint8 combatStyle; // CombatStyle specific style of combat uint40 petId; // id of the pet (can be empty) } struct QueuedAction { uint16 actionId; uint16 regenerateId; // Food (combat), maybe something for non-combat later uint16 choiceId; // Melee/Ranged/Magic (combat), logs, ore (non-combat) uint16 rightHandEquipmentTokenId; // Axe/Sword/bow, can be empty uint16 leftHandEquipmentTokenId; // Shield, can be empty uint24 timespan; // How long to queue the action for uint24 prevProcessedTime; // How long the action has been processed for previously uint24 prevProcessedXPTime; // How much XP has been gained for this action so far uint64 queueId; // id of this queued action bytes1 packed; // 1st bit is isValid (not used yet), 2nd bit is for hasPet (decides if the 2nd storage slot is read) uint8 combatStyle; uint24 reserved; // Next storage slot uint40 petId; // id of the pet (can be empty) } // This is only used as an input arg (and events) struct ActionInput { uint16 actionId; ActionInfo info; GuaranteedReward[] guaranteedRewards; RandomReward[] randomRewards; CombatStats combatStats; } struct ActionInfo { uint8 skill; bool actionChoiceRequired; // If true, then the user must choose an action choice uint24 xpPerHour; uint32 minXP; uint24 numSpawned; // Mostly for combat, capped respawn rate for xp/drops. Per hour, base 10000 uint16 handItemTokenIdRangeMin; // Inclusive uint16 handItemTokenIdRangeMax; // Inclusive uint8 successPercent; // 0-100 uint8 worldLocation; // 0 is the main starting world bool isFullModeOnly; bool isAvailable; uint16 questPrerequisiteId; } uint16 constant ACTIONCHOICE_MELEE_BASIC_SWORD = 1500; uint16 constant ACTIONCHOICE_MAGIC_SHADOW_BLAST = 2000; uint16 constant ACTIONCHOICE_RANGED_BASIC_BOW = 3000; // Allows for 2, 4 or 8 hour respawn time uint256 constant SPAWN_MUL = 1000; uint256 constant RATE_MUL = 1000; uint256 constant GUAR_MUL = 10; // Guaranteeded reward multiplier (1 decimal, allows for 2 hour action times) uint256 constant MAX_QUEUEABLE_ACTIONS = 3; // Available slots to queue actions
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import "./actions.sol"; import "./items.sol"; import "./misc.sol"; import "./players.sol"; import "./rewards.sol"; import "./quests.sol"; import "./promotions.sol"; import "./clans.sol"; import "./pets.sol";
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {IBank} from "../interfaces/IBank.sol"; enum ClanRank { NONE, // Not in a clan COMMONER, // Member of the clan SCOUT, // Invite and kick commoners COLONEL, // Can launch attacks and assign combatants TREASURER, // Can withdraw from bank LEADER, // Can edit clan details OWNER // Can do everything and transfer ownership } enum BattleResultEnum { DRAW, WIN, LOSE } struct ClanBattleInfo { uint40 lastClanIdAttackOtherClanIdCooldownTimestamp; uint8 numReattacks; uint40 lastOtherClanIdAttackClanIdCooldownTimestamp; uint8 numReattacksOtherClan; } // Packed for gas efficiency struct Vault { bool claimed; // Only applies to the first one, if it's claimed without the second one being claimed uint40 timestamp; uint80 amount; uint40 timestamp1; uint80 amount1; } struct VaultClanInfo { IBank bank; uint96 totalBrushLocked; // New storage slot uint40 attackingCooldownTimestamp; uint40 assignCombatantsCooldownTimestamp; bool currentlyAttacking; uint24 defendingVaultsOffset; uint40 blockAttacksTimestamp; uint8 blockAttacksCooldownHours; bool isInMMRArray; uint40 superAttackCooldownTimestamp; uint64[] playerIds; Vault[] defendingVaults; // Append only, and use defendingVaultsOffset to decide where the real start is } uint256 constant MAX_CLAN_COMBATANTS = 20; uint256 constant CLAN_WARS_GAS_PRICE_WINDOW_SIZE = 4; bool constant XP_EMITTED_ELSEWHERE = true;
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; uint16 constant NONE = 0; uint16 constant COMBAT_BASE = 2048; // Melee uint16 constant SWORD_BASE = COMBAT_BASE; uint16 constant BRONZE_SWORD = SWORD_BASE; // Woodcutting (2816 - 3071) uint16 constant WOODCUTTING_BASE = 2816; uint16 constant BRONZE_AXE = WOODCUTTING_BASE; // Firemaking (3328 - 3583) uint16 constant FIRE_BASE = 3328; uint16 constant MAGIC_FIRE_STARTER = FIRE_BASE; uint16 constant FIRE_MAX = FIRE_BASE + 255; // Fishing (3072 - 3327) uint16 constant FISHING_BASE = 3072; uint16 constant NET_STICK = FISHING_BASE; // Mining (2560 - 2815) uint16 constant MINING_BASE = 2560; uint16 constant BRONZE_PICKAXE = MINING_BASE; // Magic uint16 constant STAFF_BASE = COMBAT_BASE + 50; uint16 constant TOTEM_STAFF = STAFF_BASE; // Ranged uint16 constant BOW_BASE = COMBAT_BASE + 100; uint16 constant BASIC_BOW = BOW_BASE; // Cooked fish uint16 constant COOKED_FISH_BASE = 11008; uint16 constant COOKED_FEOLA = COOKED_FISH_BASE + 3; // Scrolls uint16 constant SCROLL_BASE = 12032; uint16 constant SHADOW_SCROLL = SCROLL_BASE; // Boosts uint16 constant BOOST_BASE = 12800; uint16 constant COMBAT_BOOST = BOOST_BASE; uint16 constant XP_BOOST = BOOST_BASE + 1; uint16 constant GATHERING_BOOST = BOOST_BASE + 2; uint16 constant SKILL_BOOST = BOOST_BASE + 3; uint16 constant ABSENCE_BOOST = BOOST_BASE + 4; uint16 constant LUCKY_POTION = BOOST_BASE + 5; uint16 constant LUCK_OF_THE_DRAW = BOOST_BASE + 6; uint16 constant PRAY_TO_THE_BEARDIE = BOOST_BASE + 7; uint16 constant PRAY_TO_THE_BEARDIE_2 = BOOST_BASE + 8; uint16 constant PRAY_TO_THE_BEARDIE_3 = BOOST_BASE + 9; uint16 constant BOOST_RESERVED_1 = BOOST_BASE + 10; uint16 constant BOOST_RESERVED_2 = BOOST_BASE + 11; uint16 constant BOOST_RESERVED_3 = BOOST_BASE + 12; uint16 constant GO_OUTSIDE = BOOST_BASE + 13; uint16 constant RAINING_RARES = BOOST_BASE + 14; uint16 constant CLAN_BOOSTER = BOOST_BASE + 15; uint16 constant CLAN_BOOSTER_2 = BOOST_BASE + 16; uint16 constant CLAN_BOOSTER_3 = BOOST_BASE + 17; uint16 constant BOOST_RESERVED_4 = BOOST_BASE + 18; uint16 constant BOOST_RESERVED_5 = BOOST_BASE + 19; uint16 constant BOOST_RESERVED_6 = BOOST_BASE + 20; uint16 constant BOOST_MAX = 13055; // Eggs uint16 constant EGG_BASE = 12544; uint16 constant SECRET_EGG_1_TIER1 = EGG_BASE; uint16 constant SECRET_EGG_2_TIER1 = EGG_BASE + 1; uint16 constant EGG_MAX = 12799; // Miscs uint16 constant MISC_BASE = 65535; uint16 constant RAID_PASS = MISC_BASE - 1; struct BulkTransferInfo { uint256[] tokenIds; uint256[] amounts; address to; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; enum BoostType { NONE, ANY_XP, COMBAT_XP, NON_COMBAT_XP, GATHERING, ABSENCE, PASSIVE_SKIP_CHANCE, // Clan wars PVP_BLOCK, PVP_REATTACK, PVP_SUPER_ATTACK, // Combat stats COMBAT_FIXED } struct Equipment { uint16 itemTokenId; uint24 amount; } enum Skill { NONE, COMBAT, // This is a helper which incorporates all combat skills, attack <-> magic, defence, health etc MELEE, RANGED, MAGIC, DEFENCE, HEALTH, RESERVED_COMBAT, MINING, WOODCUTTING, FISHING, SMITHING, THIEVING, CRAFTING, COOKING, FIREMAKING, FARMING, ALCHEMY, FLETCHING, FORGING, RESERVED2, RESERVED3, RESERVED4, RESERVED5, RESERVED6, RESERVED7, RESERVED8, RESERVED9, RESERVED10, RESERVED11, RESERVED12, RESERVED13, RESERVED14, RESERVED15, RESERVED16, RESERVED17, RESERVED18, RESERVED19, RESERVED20, TRAVELING // Helper Skill for travelling } struct Attire { uint16 head; uint16 neck; uint16 body; uint16 arms; uint16 legs; uint16 feet; uint16 ring; uint16 reserved1; } struct CombatStats { // From skill points int16 meleeAttack; int16 magicAttack; int16 rangedAttack; int16 health; // These include equipment int16 meleeDefence; int16 magicDefence; int16 rangedDefence; } enum CombatStyle { NONE, ATTACK, DEFENCE }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {Skill} from "./misc.sol"; enum PetSkin { NONE, DEFAULT, OG, ONEKIN, FROST, CRYSTAL, ANNIV1, KRAGSTYR } enum PetEnhancementType { NONE, MELEE, MAGIC, RANGED, DEFENCE, HEALTH, MELEE_AND_DEFENCE, MAGIC_AND_DEFENCE, RANGED_AND_DEFENCE } struct Pet { Skill skillEnhancement1; uint8 skillFixedEnhancement1; uint8 skillPercentageEnhancement1; Skill skillEnhancement2; uint8 skillFixedEnhancement2; uint8 skillPercentageEnhancement2; uint40 lastAssignmentTimestamp; address owner; // Will be used as an optimization to avoid having to look up the owner of the pet in another storage slot bool isTransferable; // New storage slot uint24 baseId; // These are used when training a pet uint40 lastTrainedTimestamp; uint8 skillFixedEnhancementMax1; // The maximum possible value for skillFixedEnhancement1 when training uint8 skillFixedEnhancementMax2; uint8 skillPercentageEnhancementMax1; uint8 skillPercentageEnhancementMax2; uint64 xp; } struct BasePetMetadata { string description; uint8 tier; PetSkin skin; PetEnhancementType enhancementType; Skill skillEnhancement1; uint8 skillFixedMin1; uint8 skillFixedMax1; uint8 skillFixedIncrement1; uint8 skillPercentageMin1; uint8 skillPercentageMax1; uint8 skillPercentageIncrement1; uint8 skillMinLevel1; Skill skillEnhancement2; uint8 skillFixedMin2; uint8 skillFixedMax2; uint8 skillFixedIncrement2; uint8 skillPercentageMin2; uint8 skillPercentageMax2; uint8 skillPercentageIncrement2; uint8 skillMinLevel2; uint16 fixedStarThreshold; uint16 percentageStarThreshold; bool isTransferable; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {QueuedAction} from "./actions.sol"; import {Skill, BoostType, CombatStats, Equipment} from "./misc.sol"; import {PlayerQuest} from "./quests.sol"; // 4 bytes for each level. 0x00000000 is the first level, 0x00000054 is the second, etc. bytes constant XP_BYTES = hex"0000000000000054000000AE0000010E00000176000001E60000025E000002DE00000368000003FD0000049B00000546000005FC000006C000000792000008730000096400000A6600000B7B00000CA400000DE100000F36000010A200001229000013CB0000158B0000176B0000196E00001B9400001DE20000205A000022FF000025D5000028DD00002C1E00002F99000033540000375200003B9A000040300000451900004A5C00004FFF0000560900005C810000637000006ADD000072D100007B570000847900008E42000098BE0000A3F90000B0020000BCE70000CAB80000D9860000E9630000FA6200010C990001201D0001350600014B6F0001637300017D2E000198C10001B64E0001D5F80001F7E600021C430002433B00026CFD000299BE0002C9B30002FD180003342B00036F320003AE730003F23D00043AE3000488BE0004DC2F0005359B000595700005FC2400066A360006E02D00075E990007E6160008774C000912EB0009B9B4000A6C74000B2C06000BF956000CD561000DC134000EBDF3000FCCD40010EF2400122648001373BF0014D9230016582C0017F2B00019AAA9001B8234001D7B95001F99390021DDBC00244BE60026E6B60029B15F002CAF51002FE43A0033540D00370303003AF5A4003F30CC0043B9B0004895E3004DCB600053609100595C53005FC6030066A585006E034D0075E86C007E5E980087703B0091287D009B935300A6BD8F00B2B4EE00BF882800CD470500DC026F00EBCC8500FCB8B7010EDBD5"; uint256 constant MAX_LEVEL = 140; // Original max level uint256 constant MAX_LEVEL_1 = 160; // TODO: Update later uint256 constant MAX_LEVEL_2 = 190; // TODO: Update later enum EquipPosition { NONE, HEAD, NECK, BODY, ARMS, LEGS, FEET, RING, SPARE2, LEFT_HAND, RIGHT_HAND, BOTH_HANDS, QUIVER, MAGIC_BAG, FOOD, AUX, // wood, seeds etc.. BOOST_VIAL, EXTRA_BOOST_VIAL, GLOBAL_BOOST_VIAL, CLAN_BOOST_VIAL, PASSIVE_BOOST_VIAL, LOCKED_VAULT, TERRITORY } struct Player { uint40 currentActionStartTimestamp; // The in-progress start time of the first queued action Skill currentActionProcessedSkill1; // The skill that the queued action has already gained XP in uint24 currentActionProcessedXPGained1; // The amount of XP that the queued action has already gained Skill currentActionProcessedSkill2; uint24 currentActionProcessedXPGained2; Skill currentActionProcessedSkill3; uint24 currentActionProcessedXPGained3; uint16 currentActionProcessedFoodConsumed; uint16 currentActionProcessedBaseInputItemsConsumedNum; // e.g scrolls, crafting materials etc Skill skillBoosted1; // The first skill that is boosted Skill skillBoosted2; // The second skill that is boosted (if applicable) uint48 totalXP; uint16 totalLevel; // Doesn't not automatically add new skills to it bytes1 packedData; // Contains worldLocation in first 6 bits (0 is the main starting randomnessBeacon), and full mode unlocked in the upper most bit // TODO: Can be up to 7 QueuedAction[] actionQueue; string name; // Raw name } struct Item { EquipPosition equipPosition; bytes1 packedData; // 0x1 exists, upper most bit is full mode uint16 questPrerequisiteId; // Can it be transferred? bool isTransferable; // TODO: Move into packedData // Food uint16 healthRestored; // Boost vial BoostType boostType; uint16 boostValue; // Varies, could be the % increase uint24 boostDuration; // How long the effect of the boost last // Combat stats int16 meleeAttack; int16 magicAttack; int16 rangedAttack; int16 meleeDefence; int16 magicDefence; int16 rangedDefence; int16 health; // Minimum requirements in this skill to use this item (can be NONE) Skill skill; uint32 minXP; } // Used for events struct BoostInfo { uint40 startTime; uint24 duration; uint16 value; uint16 itemTokenId; // Get the effect of it BoostType boostType; } struct PlayerBoostInfo { uint40 startTime; uint24 duration; uint16 value; uint16 itemTokenId; // Get the effect of it BoostType boostType; // Another boost slot (for global/clan boosts this is the "last", for users it is the "extra") uint40 extraOrLastStartTime; uint24 extraOrLastDuration; uint16 extraOrLastValue; uint16 extraOrLastItemTokenId; BoostType extraOrLastBoostType; uint40 cooldown; // Just put here for packing } // This is effectively a ratio to produce 1 of outputTokenId. // Available choices that can be undertaken for an action struct ActionChoiceInput { uint8 skill; // Skill that this action choice is related to uint24 rate; // Rate of output produced per hour (base 1000) 3 decimals uint24 xpPerHour; uint16[] inputTokenIds; uint24[] inputAmounts; uint16 outputTokenId; uint8 outputAmount; uint8 successPercent; // 0-100 uint16 handItemTokenIdRangeMin; // Inclusive uint16 handItemTokenIdRangeMax; // Inclusive bool isFullModeOnly; bool isAvailable; uint16 questPrerequisiteId; uint8[] skills; // Skills required to do this action choice uint32[] skillMinXPs; // Min XP in the corresponding skills to be able to do this action choice int16[] skillDiffs; // How much the skill is increased/decreased by this action choice } struct ActionChoice { uint8 skill; // Skill that this action choice is related to uint24 rate; // Rate of output produced per hour (base 1000) 3 decimals uint24 xpPerHour; uint16 inputTokenId1; uint24 inputAmount1; uint16 inputTokenId2; uint24 inputAmount2; uint16 inputTokenId3; uint24 inputAmount3; uint16 outputTokenId; uint8 outputAmount; uint8 successPercent; // 0-100 uint8 skill1; // Skills required to do this action choice, commonly the same as skill uint32 skillMinXP1; // Min XP in the skill to be able to do this action choice int16 skillDiff1; // How much the skill is increased/decreased by this action choice uint8 skill2; uint32 skillMinXP2; int16 skillDiff2; uint8 skill3; uint32 skillMinXP3; int16 skillDiff3; uint16 handItemTokenIdRangeMin; // Inclusive uint16 handItemTokenIdRangeMax; // Inclusive uint16 questPrerequisiteId; // FullMode is last bit, first 6 bits is worldLocation, // 2nd last bit is if there are other skills in next storage slot to check, // 3rd last bit if the input amounts should be used bytes1 packedData; } // Must be in the same order as Skill enum struct PackedXP { uint40 melee; uint40 ranged; uint40 magic; uint40 defence; uint40 health; uint40 reservedCombat; bytes2 packedDataIsMaxed; // 2 bits per skill to indicate whether the maxed skill is reached. I think this was added in case we added a new max level which a user had already passed so old & new levels are the same and it would not trigger a level up event. // Next slot uint40 mining; uint40 woodcutting; uint40 fishing; uint40 smithing; uint40 thieving; uint40 crafting; bytes2 packedDataIsMaxed1; // 2 bits per skill to indicate whether the maxed skill is reached // Next slot uint40 cooking; uint40 firemaking; uint40 farming; uint40 alchemy; uint40 fletching; uint40 forging; bytes2 packedDataIsMaxed2; // 2 bits per skill to indicate whether the maxed skill is reached } struct AvatarInfo { string name; string description; string imageURI; Skill[2] startSkills; // Can be NONE } struct PastRandomRewardInfo { uint16 itemTokenId; uint24 amount; uint64 queueId; } struct PendingQueuedActionEquipmentState { uint256[] consumedItemTokenIds; uint256[] consumedAmounts; uint256[] producedItemTokenIds; uint256[] producedAmounts; } struct PendingQueuedActionMetadata { uint32 xpGained; // total xp gained uint32 rolls; bool died; uint16 actionId; uint64 queueId; uint24 elapsedTime; uint24 xpElapsedTime; uint8 checkpoint; } struct PendingQueuedActionData { // The amount of XP that the queued action has already gained Skill skill1; uint24 xpGained1; Skill skill2; // Most likely health uint24 xpGained2; Skill skill3; // Could come uint24 xpGained3; // How much food is consumed in the current action so far uint16 foodConsumed; // How many base consumables are consumed in the current action so far uint16 baseInputItemsConsumedNum; } struct PendingQueuedActionProcessed { // XP gained during this session Skill[] skills; uint32[] xpGainedSkills; // Data for the current action which has been previously processed, this is used to store on the Player PendingQueuedActionData currentAction; } struct QuestState { uint256[] consumedItemTokenIds; uint256[] consumedAmounts; uint256[] rewardItemTokenIds; uint256[] rewardAmounts; PlayerQuest[] activeQuestInfo; uint256[] questsCompleted; Skill[] skills; // Skills gained XP in uint32[] xpGainedSkills; // XP gained in these skills } struct LotteryWinnerInfo { uint16 lotteryId; uint24 raffleId; uint16 itemTokenId; uint16 amount; bool instantConsume; uint64 playerId; } struct PendingQueuedActionState { // These 2 are in sync. Separated to reduce gas/deployment costs as these are passed down many layers. PendingQueuedActionEquipmentState[] equipmentStates; PendingQueuedActionMetadata[] actionMetadatas; QueuedAction[] remainingQueuedActions; PastRandomRewardInfo[] producedPastRandomRewards; uint256[] xpRewardItemTokenIds; uint256[] xpRewardAmounts; uint256[] dailyRewardItemTokenIds; uint256[] dailyRewardAmounts; PendingQueuedActionProcessed processedData; bytes32 dailyRewardMask; QuestState quests; uint256 numPastRandomRewardInstancesToRemove; uint8 worldLocation; LotteryWinnerInfo lotteryWinner; } struct FullAttireBonusInput { Skill skill; uint8 bonusXPPercent; uint8 bonusRewardsPercent; // 3 = 3% uint16[5] itemTokenIds; // 0 = head, 1 = body, 2 arms, 3 body, 4 = feet } // Contains everything you need to create an item struct ItemInput { CombatStats combatStats; uint16 tokenId; EquipPosition equipPosition; bool isTransferable; bool isFullModeOnly; bool isAvailable; uint16 questPrerequisiteId; // Minimum requirements in this skill Skill skill; uint32 minXP; // Food uint16 healthRestored; // Boost BoostType boostType; uint16 boostValue; // Varies, could be the % increase uint24 boostDuration; // How long the effect of the boost vial last // uri string metadataURI; string name; } /* Order head, neck, body, arms, legs, feet, ring, reserved1, leftHandEquipment, rightHandEquipment, Not used yet: input1, input2,input3, regenerate, reserved2, reserved3 */ struct CheckpointEquipments { uint16[16] itemTokenIds; uint16[16] balances; } struct ActivePlayerInfo { uint64 playerId; uint40 checkpoint; uint24 timespan; uint24 timespan1; uint24 timespan2; } uint8 constant START_LEVEL = 17; // Needs updating when there is a new skill. Only useful for new heroes. uint256 constant MAX_UNIQUE_TICKETS = 64; // Used in a bunch of places uint256 constant IS_FULL_MODE_BIT = 7; // Passive/Instant/InstantVRF/Actions/ActionChoices/Item action uint256 constant IS_AVAILABLE_BIT = 6; // Passive actions uint256 constant HAS_RANDOM_REWARDS_BIT = 5; // The rest use world location for first 4 bits // Queued action uint256 constant HAS_PET_BIT = 2; uint256 constant IS_VALID_BIT = 1;
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; enum Promotion { NONE, STARTER, HALLOWEEN_2023, XMAS_2023, HALLOWEEN_2024, HOLIDAY4, // Just have placeholders for now HOLIDAY5, HOLIDAY6, HOLIDAY7, HOLIDAY8, HOLIDAY9, HOLIDAY10 } enum PromotionMintStatus { NONE, SUCCESS, PROMOTION_ALREADY_CLAIMED, ORACLE_NOT_CALLED, MINTING_OUTSIDE_AVAILABLE_DATE, PLAYER_DOES_NOT_QUALIFY, PLAYER_NOT_HIT_ENOUGH_CLAIMS_FOR_STREAK_BONUS, DEPENDENT_QUEST_NOT_COMPLETED } struct PromotionInfoInput { Promotion promotion; uint40 startTime; uint40 endTime; // Exclusive uint8 numDailyRandomItemsToPick; // Number of items to pick uint40 minTotalXP; // Minimum xp required to claim uint256 tokenCost; // Cost in brush to start the promotion, max 16mil // Special promotion specific (like 1kin) uint8 redeemCodeLength; // Length of the redeem code bool adminOnly; // Only admins can mint the promotion, like for 1kin (Not used yet) bool promotionTiedToUser; // If the promotion is tied to a user bool promotionTiedToPlayer; // If the promotion is tied to the player bool promotionMustOwnPlayer; // Must own the player to get the promotion // Evolution specific bool evolvedHeroOnly; // Only allow evolved heroes to claim // Multiday specific bool isMultiday; // The promotion is multi-day uint256 brushCostMissedDay; // Cost in brush to mint the promotion if they miss a day (in ether), max 25.6 (base 100) uint8 numDaysHitNeededForStreakBonus; // How many days to hit for the streak bonus uint8 numDaysClaimablePeriodStreakBonus; // If there is a streak bonus, how many days to claim it after the promotion ends. If no final day bonus, set to 0 uint8 numRandomStreakBonusItemsToPick1; // Number of items to pick for the streak bonus uint8 numRandomStreakBonusItemsToPick2; // Number of random items to pick for the streak bonus uint16[] randomStreakBonusItemTokenIds1; uint32[] randomStreakBonusAmounts1; uint16[] randomStreakBonusItemTokenIds2; uint32[] randomStreakBonusAmounts2; uint16[] guaranteedStreakBonusItemTokenIds; uint16[] guaranteedStreakBonusAmounts; // Single and multiday uint16[] guaranteedItemTokenIds; // Guaranteed items for the promotions each day, if empty then they are handled in a specific way for the promotion like daily rewards uint32[] guaranteedAmounts; // Corresponding amounts to the itemTokenIds uint16[] randomItemTokenIds; // Possible items for the promotions each day, if empty then they are handled in a specific way for the promotion like daily rewards uint32[] randomAmounts; // Corresponding amounts to the randomItemTokenIds // Quests uint16 questPrerequisiteId; } struct PromotionInfo { Promotion promotion; uint40 startTime; uint8 numDays; uint8 numDailyRandomItemsToPick; // Number of items to pick uint40 minTotalXP; // Minimum xp required to claim uint24 tokenCost; // Cost in brush to mint the promotion (in ether), max 16mil // Quests uint16 questPrerequisiteId; // Special promotion specific (like 1kin), could pack these these later uint8 redeemCodeLength; // Length of the redeem code bool adminOnly; // Only admins can mint the promotion, like for 1kin bool promotionTiedToUser; // If the promotion is tied to a user bool promotionTiedToPlayer; // If the promotion is tied to the player bool promotionMustOwnPlayer; // Must own the player to get the promotion // Evolution specific bool evolvedHeroOnly; // Only allow evolved heroes to claim // Multiday specific bool isMultiday; // The promotion is multi-day uint8 brushCostMissedDay; // Cost in brush to mint the promotion if they miss a day (in ether), max 25.5, base 100 uint8 numDaysHitNeededForStreakBonus; // How many days to hit for the streak bonus uint8 numDaysClaimablePeriodStreakBonus; // If there is a streak bonus, how many days to claim it after the promotion ends. If no final day bonus, set to 0 uint8 numRandomStreakBonusItemsToPick1; // Number of items to pick for the streak bonus uint8 numRandomStreakBonusItemsToPick2; // Number of random items to pick for the streak bonus // Misc uint16[] randomStreakBonusItemTokenIds1; uint32[] randomStreakBonusAmounts1; uint16[] randomStreakBonusItemTokenIds2; // Not used yet uint32[] randomStreakBonusAmounts2; // Not used yet uint16[] guaranteedStreakBonusItemTokenIds; // Not used yet uint16[] guaranteedStreakBonusAmounts; // Not used yet // Single and multiday uint16[] guaranteedItemTokenIds; // Guaranteed items for the promotions each day, if empty then they are handled in a specific way for the promotion like daily rewards uint32[] guaranteedAmounts; // Corresponding amounts to the itemTokenIds uint16[] randomItemTokenIds; // Possible items for the promotions each day, if empty then they are handled in a specific way for the promotion like daily rewards uint32[] randomAmounts; // Corresponding amounts to the randomItemTokenIds } uint256 constant BRUSH_COST_MISSED_DAY_MUL = 10;
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {Skill} from "./misc.sol"; struct QuestInput { uint16 dependentQuestId; // The quest that must be completed before this one can be started uint16 actionId1; // action to do uint16 actionNum1; // how many (up to 65535) uint16 actionId2; // another action to do uint16 actionNum2; // how many (up to 65535) uint16 actionChoiceId; // actionChoice to perform uint16 actionChoiceNum; // how many to do (base number), (up to 65535) Skill skillReward; // The skill to reward XP to uint24 skillXPGained; // The amount of XP to give (up to 65535) uint16 rewardItemTokenId1; // Reward an item uint16 rewardAmount1; // amount of the reward (up to 65535) uint16 rewardItemTokenId2; // Reward another item uint16 rewardAmount2; // amount of the reward (up to 65535) uint16 burnItemTokenId; // Burn an item uint16 burnAmount; // amount of the burn (up to 65535) uint16 questId; // Unique id for this quest bool isFullModeOnly; // If true this quest requires the user be evolved uint8 worldLocation; // 0 is the main starting world } struct Quest { uint16 dependentQuestId; // The quest that must be completed before this one can be started uint16 actionId1; // action to do uint16 actionNum1; // how many (up to 65535) uint16 actionId2; // another action to do uint16 actionNum2; // how many (up to 65535) uint16 actionChoiceId; // actionChoice to perform uint16 actionChoiceNum; // how many to do (base number), (up to 65535) Skill skillReward; // The skill to reward XP to uint24 skillXPGained; // The amount of XP to give (up to 65535) uint16 rewardItemTokenId1; // Reward an item uint16 rewardAmount1; // amount of the reward (up to 65535) uint16 rewardItemTokenId2; // Reward another item uint16 rewardAmount2; // amount of the reward (up to 65535) uint16 burnItemTokenId; // Burn an item uint16 burnAmount; // amount of the burn (up to 65535) uint16 reserved; // Reserved for future use (previously was questId and cleared) bytes1 packedData; // FullMode is last bit, first 6 bits is worldLocation } struct PlayerQuest { uint32 questId; uint16 actionCompletedNum1; uint16 actionCompletedNum2; uint16 actionChoiceCompletedNum; uint16 burnCompletedAmount; } uint256 constant QUEST_PURSE_STRINGS = 5; // MAKE SURE THIS MATCHES definitions
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {BoostType, Equipment} from "./misc.sol"; struct GuaranteedReward { uint16 itemTokenId; uint16 rate; // num per hour (base 10, 1 decimal) for actions and num per duration for passive actions } struct RandomReward { uint16 itemTokenId; uint16 chance; // out of 65535 uint8 amount; // out of 255 } struct PendingRandomReward { uint16 actionId; uint40 startTime; uint24 xpElapsedTime; uint16 boostItemTokenId; uint24 elapsedTime; uint40 boostStartTime; // When the boost was started uint24 sentinelElapsedTime; // Full equipment at the time this was generated uint8 fullAttireBonusRewardsPercent; uint64 queueId; // TODO: Could reduce this if more stuff is needed } struct ActionRewards { uint16 guaranteedRewardTokenId1; uint16 guaranteedRewardRate1; // Num per hour base 10 (1 decimal) for actions (Max 6553.5 per hour), num per duration for passive actions uint16 guaranteedRewardTokenId2; uint16 guaranteedRewardRate2; uint16 guaranteedRewardTokenId3; uint16 guaranteedRewardRate3; // Random chance rewards uint16 randomRewardTokenId1; uint16 randomRewardChance1; // out of 65535 uint8 randomRewardAmount1; // out of 255 uint16 randomRewardTokenId2; uint16 randomRewardChance2; uint8 randomRewardAmount2; uint16 randomRewardTokenId3; uint16 randomRewardChance3; uint8 randomRewardAmount3; uint16 randomRewardTokenId4; uint16 randomRewardChance4; uint8 randomRewardAmount4; // No more room in this storage slot! } struct XPThresholdReward { uint32 xpThreshold; Equipment[] rewards; } enum InstantVRFActionType { NONE, GENERIC, FORGING, EGG } struct InstantVRFActionInput { uint16 actionId; uint16[] inputTokenIds; uint24[] inputAmounts; bytes data; InstantVRFActionType actionType; bool isFullModeOnly; bool isAvailable; uint16 questPrerequisiteId; } struct InstantVRFRandomReward { uint16 itemTokenId; uint16 chance; // out of 65535 uint16 amount; // out of 65535 } uint256 constant MAX_GUARANTEED_REWARDS_PER_ACTION = 3; uint256 constant MAX_RANDOM_REWARDS_PER_ACTION = 4; uint256 constant MAX_REWARDS_PER_ACTION = MAX_GUARANTEED_REWARDS_PER_ACTION + MAX_RANDOM_REWARDS_PER_ACTION; uint256 constant MAX_CONSUMED_PER_ACTION = 3; uint256 constant MAX_QUEST_REWARDS = 2; uint256 constant TIER_1_DAILY_REWARD_START_XP = 0; uint256 constant TIER_2_DAILY_REWARD_START_XP = 7_650; uint256 constant TIER_3_DAILY_REWARD_START_XP = 33_913; uint256 constant TIER_4_DAILY_REWARD_START_XP = 195_864; uint256 constant TIER_5_DAILY_REWARD_START_XP = 784_726; uint256 constant TIER_6_DAILY_REWARD_START_XP = 2_219_451; // 4 bytes for each threshold, starts at 500 xp in decimal bytes constant XP_THRESHOLD_REWARDS = hex"00000000000001F4000003E8000009C40000138800002710000075300000C350000186A00001D4C0000493E0000557300007A120000927C0000B71B0000DBBA0000F424000124F800016E360001B7740001E8480002625A0002932E0002DC6C0003567E0003D0900004C4B40005B8D80006ACFC0007A1200008954400098968000A7D8C000B71B0000C65D4000D59F8000E4E1C0";
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IBrushToken is IERC20 { function burn(uint256 amount) external; function burnFrom(address account, uint256 amount) external; function transferFromBulk(address from, address[] calldata tos, uint256[] calldata amounts) external; function transferOwnership(address newOwner) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; interface IMarketplaceWhitelist { function isWhitelisted(address nft) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; interface IBank { function initialize() external; function initializeAddresses( uint256 clanId, address bankRegistry, address bankRelay, address playerNFT, address itemNFT, address clans, address players, address lockedBankVaults, address raids ) external; function depositToken(address sender, address from, uint256 playerId, address token, uint256 amount) external; function setAllowBreachedCapacity(bool allow) external; }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.28; interface IBankFactory { function getBankAddress(uint256 clanId) external view returns (address); function getCreatedHere(address bank) external view returns (bool); function createBank(address from, uint256 clanId) external returns (address); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; interface IClanMemberLeftCB { function clanMemberLeft(uint256 clanId, uint256 playerId) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {ClanRank} from "../globals/clans.sol"; interface IClans { function canWithdraw(uint256 clanId, uint256 playerId) external view returns (bool); function isClanMember(uint256 clanId, uint256 playerId) external view returns (bool); function maxBankCapacity(uint256 clanId) external view returns (uint16); function maxMemberCapacity(uint256 clanId) external view returns (uint16); function getRank(uint256 clanId, uint256 playerId) external view returns (ClanRank); function setMMR(uint256 clanId, uint16 mmr) external; function getMMR(uint256 clanId) external view returns (uint16); function addXP(uint256 clanId, uint40 xp, bool xpEmittedElsewhere) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import "../globals/misc.sol"; import "../globals/players.sol"; interface IPlayers { function clearEverythingBeforeTokenTransfer(address from, uint256 tokenId) external; function beforeTokenTransferTo(address to, uint256 tokenId) external; function getURI( uint256 playerId, string calldata name, string calldata avatarName, string calldata avatarDescription, string calldata imageURI ) external view returns (string memory); function mintedPlayer( address from, uint256 playerId, Skill[2] calldata startSkills, bool makeActive, uint256[] calldata startingItemTokenIds, uint256[] calldata startingAmounts ) external; function upgradePlayer(uint256 playerId) external; function isPlayerEvolved(uint256 playerId) external view returns (bool); function isOwnerOfPlayerAndActive(address from, uint256 playerId) external view returns (bool); function getAlphaCombatParams() external view returns (uint8 alphaCombat, uint8 betaCombat, uint8 alphaCombatHealing); function getActivePlayer(address owner) external view returns (uint256 playerId); function getPlayerXP(uint256 playerId, Skill skill) external view returns (uint256 xp); function getLevel(uint256 playerId, Skill skill) external view returns (uint256 level); function getTotalXP(uint256 playerId) external view returns (uint256 totalXP); function getTotalLevel(uint256 playerId) external view returns (uint256 totalLevel); function getActiveBoost(uint256 playerId) external view returns (PlayerBoostInfo memory); function modifyXP(address from, uint256 playerId, Skill skill, uint56 xp, bool skipEffects) external; function beforeItemNFTTransfer(address from, address to, uint256[] calldata ids, uint256[] calldata amounts) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import "@openzeppelin/contracts/utils/structs/BitMaps.sol"; library BloomFilter { using BitMaps for BitMaps.BitMap; struct Filter { uint8 hashCount; // Number of hash functions to use uint64 bitCount; // Number of bits in the bitmap BitMaps.BitMap bitmap; // Bitmap using OpenZeppelin’s BitMaps library to support up to 65,536 bits } error ZeroHashCount(); /** * @notice Calculates the optimal number of hash functions based on the expected number of items. * @param expectedItems Expected number of items to be added to the filter. * @param bitCount Number of bits in the bitmap. * @return hashCount The number of hash functions to be used. */ function _getOptimalHashCount(uint256 expectedItems, uint64 bitCount) internal pure returns (uint8 hashCount) { uint256 calculatedHashCount = (bitCount * 144) / (expectedItems * 100) + 1; hashCount = calculatedHashCount < 256 ? uint8(calculatedHashCount) : 255; } /** * @notice Adds a `bytes32` item to the filter by setting bits in the bitmap. * @param filter The Bloom filter to update. * @param item Hash value of the item to add. */ function _add(Filter storage filter, bytes32 item) internal { require(filter.hashCount != 0, ZeroHashCount()); uint64 bitCount = filter.bitCount; for (uint8 i = 0; i < filter.hashCount; ++i) { uint256 position = uint256(keccak256(abi.encodePacked(item, i))) % bitCount; filter.bitmap.set(position); // Set the bit in the bitmap at the calculated position } } /** * @notice Adds a string to the filter by hashing it and setting bits in the bitmap. * @param filter The Bloom filter to update. * @param item String to add to the filter. */ function _addString(Filter storage filter, string memory item) internal { bytes32 itemHash = keccak256(abi.encodePacked(item)); _add(filter, itemHash); } /** * @notice Removes a `bytes32` item from the filter by clearing bits in the bitmap. * @param filter The Bloom filter to update. * @param item Hash value of the item to remove. */ function _remove(Filter storage filter, bytes32 item) internal { require(filter.hashCount != 0, ZeroHashCount()); uint64 bitCount = filter.bitCount; for (uint8 i = 0; i < filter.hashCount; ++i) { uint256 position = uint256(keccak256(abi.encodePacked(item, i))) % bitCount; filter.bitmap.unset(position); // Clear the bit in the bitmap at the calculated position } } /** * @notice Removes a string from the filter by hashing it and clearing bits in the bitmap. * @param filter The Bloom filter to update. * @param item String to remove from the filter. */ function _removeString(Filter storage filter, string memory item) internal { bytes32 itemHash = keccak256(abi.encodePacked(item)); _remove(filter, itemHash); } /** * @notice Checks if a `bytes32` item is probably present in the filter or definitely not present. * @param filter The Bloom filter to check. * @param item Hash value of the item to check. * @return probablyPresent True if the item may exist, false if it definitely does not exist. */ function _probablyContains(Filter storage filter, bytes32 item) internal view returns (bool probablyPresent) { if (filter.hashCount == 0) revert ZeroHashCount(); uint64 bitCount = filter.bitCount; for (uint8 i = 0; i < filter.hashCount; ++i) { uint256 position = uint256(keccak256(abi.encodePacked(item, i))) % bitCount; if (!filter.bitmap.get(position)) return false; // If any bit is not set, item is not present } return true; } /** * @notice Checks if a string is probably present in the filter or definitely not present. * @param filter The Bloom filter to check. * @param item String to check in the filter. * @return probablyPresent True if the item may exist, false if it definitely does not exist. */ function _probablyContainsString( Filter storage filter, string memory item ) internal view returns (bool probablyPresent) { bytes32 itemHash = keccak256(abi.encodePacked(item)); return _probablyContains(filter, itemHash); } function _defaults(Filter storage filter) internal { filter.hashCount = 8; // The number of hash functions to use. filter.bitCount = 1024 * 32; // Default number of bits delete filter.bitmap; // Clear the bitmap } /** * @notice Initializes a Bloom filter with a specified hash count. * @param filter The Bloom filter to initialize. */ function _initialize(Filter storage filter) internal { _defaults(filter); } /** * @notice Initializes a Bloom filter with a specified hash count. * @param filter The Bloom filter to initialize. * @param hashCount The number of hash functions to use. */ function _initialize(Filter storage filter, uint8 hashCount) internal { _defaults(filter); filter.hashCount = hashCount; } /** * @notice Initializes a Bloom filter with a specified hash count. * @param filter The Bloom filter to initialize. * @param hashCount The number of hash functions to use. * @param bitCount The number of bits in the bitmap. */ function _initialize(Filter storage filter, uint8 hashCount, uint64 bitCount) internal { _defaults(filter); filter.bitCount = bitCount; filter.hashCount = hashCount; } /** * @notice Initializes a Bloom filter with a specified hash count and clears the bitmap. * @param filter The Bloom filter to initialize. * @param hashCount The times to hash each item. * @param positions Array of positions to set in the bitmap. */ function _initialize(Filter storage filter, uint8 hashCount, uint256[] calldata positions) internal { _initialize(filter, hashCount); _addPositions(filter, positions); } /** * @notice Initializes a Bloom filter with a specified hash count and clears the bitmap. * @param filter The Bloom filter to initialize. * @param hashCount The number of hash functions to use. * @param bitCount The number of bits in the bitmap. * @param positions Array of positions to set in the bitmap. */ function _initialize(Filter storage filter, uint8 hashCount, uint64 bitCount, uint256[] calldata positions) internal { _initialize(filter, hashCount, bitCount); _addPositions(filter, positions); } /** * @notice Adds an array of positions to the filter by setting bits in the bitmap. * @param filter The Bloom filter to update. * @param positions Array of positions to set in the bitmap. */ function _addPositions(Filter storage filter, uint256[] calldata positions) internal { for (uint256 i = 0; i < positions.length; ++i) { filter.bitmap.set(positions[i]); } } }
{ "evmVersion": "cancun", "optimizer": { "enabled": true, "runs": 320, "details": { "yul": true } }, "viaIR": true, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": { "contracts/EstforLibrary.sol": { "EstforLibrary": "0xe3223eaf0e260b54a8ce777ac9f4a972310370c0" } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[],"name":"AlreadyInClan","type":"error"},{"inputs":[],"name":"AlreadySentInvite","type":"error"},{"inputs":[],"name":"AlreadySentJoinRequest","type":"error"},{"inputs":[],"name":"BankCapacityTooLow","type":"error"},{"inputs":[],"name":"BankFactoryAlreadySet","type":"error"},{"inputs":[],"name":"CannotDowngradeTier","type":"error"},{"inputs":[],"name":"CannotRenounceToSelf","type":"error"},{"inputs":[],"name":"CannotSetSameRank","type":"error"},{"inputs":[],"name":"ChangingRankEqualOrHigherThanSelf","type":"error"},{"inputs":[],"name":"ChangingRankOfPlayerEqualOrHigherThanSelf","type":"error"},{"inputs":[],"name":"ChangingRankOfPlayerHigherThanSelf","type":"error"},{"inputs":[],"name":"ClanDestroyFailedHasMembers","type":"error"},{"inputs":[],"name":"ClanDoesNotExist","type":"error"},{"inputs":[],"name":"ClanIsFull","type":"error"},{"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"ClanNameIsReserved","type":"error"},{"inputs":[],"name":"DiscordInvalidCharacters","type":"error"},{"inputs":[],"name":"DiscordTooLong","type":"error"},{"inputs":[],"name":"DiscordTooShort","type":"error"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"ERC1967InvalidImplementation","type":"error"},{"inputs":[],"name":"ERC1967NonPayable","type":"error"},{"inputs":[],"name":"FailedCall","type":"error"},{"inputs":[],"name":"ImageIdTooLow","type":"error"},{"inputs":[],"name":"InvalidImageId","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidNFTType","type":"error"},{"inputs":[],"name":"InviteDoesNotExist","type":"error"},{"inputs":[],"name":"JoinRequestsDisabled","type":"error"},{"inputs":[],"name":"MemberCapacityTooLow","type":"error"},{"inputs":[],"name":"MessageTooLong","type":"error"},{"inputs":[],"name":"NFTNotWhitelistedOnMarketplace","type":"error"},{"inputs":[],"name":"NameAlreadyExists","type":"error"},{"inputs":[],"name":"NameInvalidCharacters","type":"error"},{"inputs":[],"name":"NameTooLong","type":"error"},{"inputs":[],"name":"NameTooShort","type":"error"},{"inputs":[],"name":"NoGateKeptNFTFound","type":"error"},{"inputs":[],"name":"NoInvitesToDelete","type":"error"},{"inputs":[],"name":"NoJoinRequest","type":"error"},{"inputs":[],"name":"NoJoinRequestsToDelete","type":"error"},{"inputs":[],"name":"NotBridge","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"NotMMRSetter","type":"error"},{"inputs":[],"name":"NotMemberOfClan","type":"error"},{"inputs":[],"name":"NotOwnerOfPlayer","type":"error"},{"inputs":[],"name":"NotOwnerOfPlayerAndActive","type":"error"},{"inputs":[],"name":"NotXPModifier","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"OwnerExists","type":"error"},{"inputs":[],"name":"PercentNotTotal100","type":"error"},{"inputs":[],"name":"PlayersAlreadySet","type":"error"},{"inputs":[],"name":"PriceTooLow","type":"error"},{"inputs":[],"name":"RankMustBeLowerRenounce","type":"error"},{"inputs":[],"name":"RankNotHighEnough","type":"error"},{"inputs":[],"name":"TelegramInvalidCharacters","type":"error"},{"inputs":[],"name":"TelegramTooLong","type":"error"},{"inputs":[],"name":"TierAlreadyExists","type":"error"},{"inputs":[],"name":"TierDoesNotExist","type":"error"},{"inputs":[],"name":"TooManyNFTs","type":"error"},{"inputs":[],"name":"TwitterInvalidCharacters","type":"error"},{"inputs":[],"name":"TwitterTooLong","type":"error"},{"inputs":[],"name":"UUPSUnauthorizedCallContext","type":"error"},{"inputs":[{"internalType":"bytes32","name":"slot","type":"bytes32"}],"name":"UUPSUnsupportedProxiableUUID","type":"error"},{"inputs":[],"name":"UnsupportedNFTType","type":"error"},{"inputs":[],"name":"ZeroHashCount","type":"error"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint8","name":"id","type":"uint8"},{"internalType":"uint16","name":"maxMemberCapacity","type":"uint16"},{"internalType":"uint16","name":"maxBankCapacity","type":"uint16"},{"internalType":"uint24","name":"maxImageId","type":"uint24"},{"internalType":"uint40","name":"minimumAge","type":"uint40"},{"internalType":"uint80","name":"price","type":"uint80"}],"indexed":false,"internalType":"struct Clans.Tier[]","name":"tiers","type":"tuple[]"}],"name":"AddTiers","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"xp","type":"uint256"},{"indexed":false,"internalType":"bool","name":"xpEmittedElsewhere","type":"bool"}],"name":"AddXP","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"playerId","type":"uint256"},{"indexed":false,"internalType":"string[]","name":"clanInfo","type":"string[]"},{"indexed":false,"internalType":"uint256","name":"imageId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tierId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"createdTimestamp","type":"uint256"}],"name":"ClanCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clanId","type":"uint256"}],"name":"ClanDestroyed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"playerId","type":"uint256"},{"indexed":false,"internalType":"string[]","name":"clanInfo","type":"string[]"},{"indexed":false,"internalType":"uint256","name":"imageId","type":"uint256"}],"name":"ClanEdited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"ClanOwnerLeft","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"ClanOwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"playerId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tierId","type":"uint256"}],"name":"ClanUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newCost","type":"uint256"}],"name":"EditNameCost","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint8","name":"id","type":"uint8"},{"internalType":"uint16","name":"maxMemberCapacity","type":"uint16"},{"internalType":"uint16","name":"maxBankCapacity","type":"uint16"},{"internalType":"uint24","name":"maxImageId","type":"uint24"},{"internalType":"uint40","name":"minimumAge","type":"uint40"},{"internalType":"uint80","name":"price","type":"uint80"}],"indexed":false,"internalType":"struct Clans.Tier[]","name":"tiers","type":"tuple[]"}],"name":"EditTiers","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clanId","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"nfts","type":"address[]"},{"indexed":false,"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"GateKeepNFTs","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":"clanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"InviteAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"playerId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fromPlayerId","type":"uint256"}],"name":"InviteSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clanId","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"invitedPlayerIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"deletedInvitesPlayerId","type":"uint256"}],"name":"InvitesDeletedByClan","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"clanIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"InvitesDeletedByPlayer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clanId","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"playerIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"fromPlayerId","type":"uint256"}],"name":"InvitesSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"playerId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"acceptedByPlayerId","type":"uint256"}],"name":"JoinRequestAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"JoinRequestRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"JoinRequestSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clanId","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"playerIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"acceptedByPlayerId","type":"uint256"}],"name":"JoinRequestsAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clanId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"joinRequestsEnabled","type":"bool"},{"indexed":false,"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"JoinRequestsEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clanId","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"joinRequestPlayerIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"removingJoinRequestsPlayerId","type":"uint256"}],"name":"JoinRequestsRemovedByClan","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"playerId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"removedByPlayerId","type":"uint256"}],"name":"MemberLeft","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clanId","type":"uint256"},{"indexed":false,"internalType":"string","name":"message","type":"string"},{"indexed":false,"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"PinMessage","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"memberId","type":"uint256"},{"indexed":false,"internalType":"enum ClanRank","name":"rank","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"PlayerRankUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"brushBurntPercentage","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"brushTreasuryPercentage","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"brushDevPercentage","type":"uint256"}],"name":"SetBrushDistributionPercentages","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"playerId","type":"uint256"},{"indexed":false,"internalType":"enum ClanRank","name":"clan","type":"uint8"}],"name":"SetClanRank","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"mmr","type":"uint256"}],"name":"SetInitialMMR","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mmr","type":"uint256"}],"name":"SetMMR","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"UPGRADE_INTERFACE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"},{"internalType":"uint256","name":"playerId","type":"uint256"},{"internalType":"uint256","name":"gateKeepTokenId","type":"uint256"}],"name":"acceptInvite","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"},{"internalType":"uint256[]","name":"newMemberPlayerIds","type":"uint256[]"},{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"acceptJoinRequests","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string[]","name":"names","type":"string[]"}],"name":"addReservedClanNames","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint8","name":"id","type":"uint8"},{"internalType":"uint16","name":"maxMemberCapacity","type":"uint16"},{"internalType":"uint16","name":"maxBankCapacity","type":"uint16"},{"internalType":"uint24","name":"maxImageId","type":"uint24"},{"internalType":"uint40","name":"minimumAge","type":"uint40"},{"internalType":"uint80","name":"price","type":"uint80"}],"internalType":"struct Clans.Tier[]","name":"tiers","type":"tuple[]"}],"name":"addTiers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"},{"internalType":"uint40","name":"xp","type":"uint40"},{"internalType":"bool","name":"xpEmittedElsewhere","type":"bool"}],"name":"addXP","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"},{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"canWithdraw","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"},{"internalType":"uint256","name":"memberId","type":"uint256"},{"internalType":"enum ClanRank","name":"rank","type":"uint8"},{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"changeRank","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"},{"internalType":"uint256[]","name":"memberIds","type":"uint256[]"},{"internalType":"enum ClanRank[]","name":"ranks","type":"uint8[]"},{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"changeRanks","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"},{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"claimOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"playerId","type":"uint256"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"discord","type":"string"},{"internalType":"string","name":"telegram","type":"string"},{"internalType":"string","name":"twitter","type":"string"},{"internalType":"uint16","name":"imageId","type":"uint16"},{"internalType":"uint8","name":"tierId","type":"uint8"}],"name":"createClan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"playerId","type":"uint256"},{"internalType":"uint256","name":"clanId","type":"uint256"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"discord","type":"string"},{"internalType":"string","name":"telegram","type":"string"},{"internalType":"string","name":"twitter","type":"string"},{"internalType":"uint256","name":"imageId","type":"uint256"},{"internalType":"uint256","name":"createdTimestamp","type":"uint256"},{"internalType":"uint256","name":"tierId","type":"uint256"},{"internalType":"uint256","name":"mmr","type":"uint256"},{"internalType":"bool","name":"disableJoinRequests","type":"bool"}],"name":"createClanBridge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"},{"internalType":"uint256[]","name":"invitedPlayerIds","type":"uint256[]"},{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"deleteInvitesAsClan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"clanIds","type":"uint256[]"},{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"deleteInvitesAsPlayer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"discord","type":"string"},{"internalType":"string","name":"telegram","type":"string"},{"internalType":"string","name":"twitter","type":"string"},{"internalType":"uint256","name":"imageId","type":"uint256"},{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"editClan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint8","name":"id","type":"uint8"},{"internalType":"uint16","name":"maxMemberCapacity","type":"uint16"},{"internalType":"uint16","name":"maxBankCapacity","type":"uint16"},{"internalType":"uint24","name":"maxImageId","type":"uint24"},{"internalType":"uint40","name":"minimumAge","type":"uint40"},{"internalType":"uint80","name":"price","type":"uint80"}],"internalType":"struct Clans.Tier[]","name":"tiers","type":"tuple[]"}],"name":"editTiers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"},{"components":[{"internalType":"address","name":"nft","type":"address"},{"internalType":"uint80","name":"nftType","type":"uint80"}],"internalType":"struct Clans.NFTInfo[]","name":"nftInfos","type":"tuple[]"},{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"gateKeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"}],"name":"getClan","outputs":[{"internalType":"uint64","name":"ownerPlayerId","type":"uint64"},{"internalType":"uint16","name":"imageId","type":"uint16"},{"internalType":"uint16","name":"memberCount","type":"uint16"},{"internalType":"uint40","name":"createdTimestamp","type":"uint40"},{"internalType":"uint8","name":"tierId","type":"uint8"},{"internalType":"bool","name":"disableJoinRequests","type":"bool"},{"internalType":"uint16","name":"mmr","type":"uint16"},{"internalType":"string","name":"name","type":"string"},{"components":[{"internalType":"address","name":"nft","type":"address"},{"internalType":"uint80","name":"nftType","type":"uint80"}],"internalType":"struct Clans.NFTInfo[]","name":"gateKeptNFTs","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"getClanId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"getClanIdFromPlayer","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"getClanNameOfPlayer","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"getClanTierMembership","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEditNameCost","outputs":[{"internalType":"uint80","name":"","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"getLowercaseNames","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"}],"name":"getMMR","outputs":[{"internalType":"uint16","name":"mmr","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"getPlayerInfo","outputs":[{"components":[{"internalType":"uint40","name":"clanId","type":"uint40"},{"internalType":"enum ClanRank","name":"rank","type":"uint8"},{"internalType":"uint40","name":"requestedClanId","type":"uint40"}],"internalType":"struct Clans.PlayerInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"},{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"getRank","outputs":[{"internalType":"enum ClanRank","name":"rank","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tierId","type":"uint256"}],"name":"getTier","outputs":[{"components":[{"internalType":"uint8","name":"id","type":"uint8"},{"internalType":"uint16","name":"maxMemberCapacity","type":"uint16"},{"internalType":"uint16","name":"maxBankCapacity","type":"uint16"},{"internalType":"uint24","name":"maxImageId","type":"uint24"},{"internalType":"uint40","name":"minimumAge","type":"uint40"},{"internalType":"uint80","name":"price","type":"uint80"}],"internalType":"struct Clans.Tier","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"},{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"hasInviteRequest","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IBrushToken","name":"brush","type":"address"},{"internalType":"contract IERC1155","name":"playerNFT","type":"address"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"dev","type":"address"},{"internalType":"uint80","name":"editNameCost","type":"uint80"},{"internalType":"address","name":"paintswapMarketplaceWhitelist","type":"address"},{"internalType":"uint16","name":"initialMMR","type":"uint16"},{"internalType":"uint40","name":"startClanId","type":"uint40"},{"internalType":"address","name":"bridge","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IPlayers","name":"players","type":"address"},{"internalType":"contract IBankFactory","name":"bankFactory","type":"address"},{"internalType":"contract IClanMemberLeftCB","name":"territories","type":"address"},{"internalType":"contract IClanMemberLeftCB","name":"lockedBankVaults","type":"address"},{"internalType":"contract IClanMemberLeftCB","name":"raids","type":"address"}],"name":"initializeAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"},{"internalType":"uint256[]","name":"memberPlayerIds","type":"uint256[]"},{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"inviteMembers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"},{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"isClanMember","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"clanName","type":"string"}],"name":"isClanNameReserved","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"isMemberOfAnyClan","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"}],"name":"maxBankCapacity","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"}],"name":"maxMemberCapacity","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"},{"internalType":"string","name":"message","type":"string"},{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"pinMessage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"},{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"removeJoinRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"},{"internalType":"uint256[]","name":"joinRequestPlayerIds","type":"uint256[]"},{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"removeJoinRequestsAsClan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"},{"internalType":"uint256","name":"newOwnerPlayerId","type":"uint256"},{"internalType":"enum ClanRank","name":"newRank","type":"uint8"}],"name":"renounceOwnershipTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"},{"internalType":"uint256","name":"playerId","type":"uint256"},{"internalType":"uint256","name":"gateKeepTokenId","type":"uint256"}],"name":"requestToJoin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"brushBurntPercentage","type":"uint8"},{"internalType":"uint8","name":"brushTreasuryPercentage","type":"uint8"},{"internalType":"uint8","name":"brushDevPercentage","type":"uint8"}],"name":"setBrushDistributionPercentages","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint80","name":"editNameCost","type":"uint80"}],"name":"setEditNameCost","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"mmr","type":"uint16"}],"name":"setInitialMMR","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"},{"internalType":"bool","name":"joinRequestsEnabled","type":"bool"},{"internalType":"uint256","name":"playerId","type":"uint256"}],"name":"setJoinRequestsEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"},{"internalType":"uint16","name":"mmr","type":"uint16"}],"name":"setMMR","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"positions","type":"uint256[]"}],"name":"setReservedNameBits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"bool","name":"isModifier","type":"bool"}],"name":"setXPModifiers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"clanId","type":"uint256"},{"internalType":"uint256","name":"playerId","type":"uint256"},{"internalType":"uint8","name":"newTierId","type":"uint8"}],"name":"upgradeClan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"}]
Contract Creation Code
60a080604052346100c257306080525f5160206159825f395f51905f525460ff8160401c166100b3576002600160401b03196001600160401b03821601610060575b6040516158bb90816100c78239608051818181611dbb0152611f5f0152f35b6001600160401b0319166001600160401b039081175f5160206159825f395f51905f525581527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f80610041565b63f92ee8a960e01b5f5260045ffd5b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c806303db62b014613a3057806305d1e1dd146135595780630d336011146111f8578063128dd84c1461348157806313bebb82146133a95780631cfc4fb81461326f5780631dbf12bf1461306b57806324900d7c14612f5d57806326e3cc2514612f1a578063374403d714612aa1578063421f9a6e14612a425780634542c92d146128a95780634934f507146124655780634b586fa01461230f5780634c47322e146122cc5780634d0175981461225d5780634da27ac6146122325780634f062c5a1461214f5780634f1ef28614611f115780634f90b4a214611e905780634fe2e75c14611e1a57806352d1902d14611da157806357e10ace14611d7957806366db0e5114611d325780636902d21514611cb9578063715018a614611c46578063720abf89146119de578063869920191461198a578063883c90781461182d5780638d94b36b146117675780638da5cb5b146117355780638f0e15a7146115b5578063900ba78a146114e05780639c2c3fe014611339578063a2b4044e146111fd578063a77da6ba146111f8578063acaace59146111bd578063ad3cb1cc14611172578063ad3e6e1414610fd0578063b35218c014610f45578063b4e4568c14610f14578063bf01c65014610e8a578063bfecf6ce14610d14578063c208e2de14610ceb578063c9fe814b14610c0a578063cf73b2c814610a02578063cfe564c9146108a9578063d7f71ac3146106af578063d940ef7014610678578063e0cc3eb61461056c578063e46cf7a7146103f1578063ea17532e1461033e578063eb9f15be146102e8578063f2fde38b146102bd5763ff2c581414610276575f80fd5b346102b95760203660031901126102b9576004355f52600a60205260ff60405f205460881c165f52600c602052602061ffff60405f205460081c16604051908152f35b5f80fd5b346102b95760203660031901126102b9576102e66102d9613dd9565b6102e1614d7f565b61450e565b005b346102b95760203660031901126102b9576004356001600160401b0381116102b95760ff602061031d81933690600401613dfe565b91908260405193849283378101600d81520301902054166040519015158152f35b346102b95760203660031901126102b9575f6040805161035d81613f13565b82815282602082015201526004355f52600b60205260405f206040519061038382613f13565b549064ffffffffff8216815260ff8260281c1691602082019060078410156103dd576103d48260609564ffffffffff945283604086019360301c168352836040519551168552516020850190613ff2565b51166040820152f35b634e487b7160e01b5f52602160045260245ffd5b346102b95760203660031901126102b9576004356001600160401b0381116102b957610421903690600401613e91565b9061042a614d7f565b5f91601e1982360301915b818410156102e6578360051b810135838112156102b95781018035906001600160401b0382116102b9576020019080360382136102b9576104809161047b913691613f6a565b6151ba565b6040516104ac6020828180820195805191829101875e81015f838201520301601f198101835282613f2e565b5190209160105460ff81161561055d5760081c6001600160401b0316935f5b60ff6010541660ff8216101561054d57600160ff91610546610529898960405161052181610513886020830195869091602192825260ff60f81b9060f81b1660208201520190565b03601f198101835282613f2e565b51902061558a565b8060081c5f526011602052600160ff60405f2092161b8154179055565b01166104cb565b5060019095019493509150610435565b63cdd17b6160e01b5f5260045ffd5b346102b95760203660031901126102b9576004355f52600a60205261ffff60405f2080549061061b6105ac60036105a5600185016143c6565b9301614484565b9160405194846001600160401b038796168652818160401c166020870152818160501c16604087015264ffffffffff8160601c16606087015260ff8160881c16608087015260ff8160901c16151560a087015260981c1660c085015261012060e0850152610120840190613fff565b828103610100840152602080835192838152019201905f5b818110610641575050500390f35b825180516001600160a01b031685526020908101516001600160501b03168186015286955060409094019390920191600101610633565b346102b95761068636613e7b565b905f52600a602052600260405f2001905f52602052602060ff60405f2054166040519015158152f35b346102b9576106bd36614023565b60015460405163e743862960e01b81523360048201526024810184905292939290602090829060449082906001600160a01b03165afa90811561089e575f9161086f575b501561086057815f52600a60205260405f205464ffffffffff8160601c16156108515760901c60ff1661084257610738908261563b565b5f828152600b60205260409020805490929064ffffffffff166108335764ffffffffff835460301c16806107bf575b83546affffffffff0000000000001916603084901b69ffffffff0000000000001617845560408051848152602081018490527f5b4036fee23699edc6c33868b48eea7f82a415ae2715865db9ec6b74cc4701459190a1005b9190818314610824577f5b4036fee23699edc6c33868b48eea7f82a415ae2715865db9ec6b74cc470145937f86ef48d3478dfc87f7a61b90ffdd3193d9862a68632a4c233e71d330b3bb5ce5604080958151908152846020820152a193509091610767565b63177366d160e01b5f5260045ffd5b6376cfa8c160e01b5f5260045ffd5b63142197d960e21b5f5260045ffd5b63d699f7e960e01b5f5260045ffd5b630a71f87f60e31b5f5260045ffd5b610891915060203d602011610897575b6108898183613f2e565b81019061403d565b84610701565b503d61087f565b6040513d5f823e3d90fd5b346102b95760603660031901126102b9576004356024356001600160401b0381116102b9576108dc903690600401613dfe565b60015460405163e743862960e01b815233600482015260448035602483018190529495939492602091839182906001600160a01b03165afa90811561089e575f916109e3575b501561086057805f52600b6020528164ffffffffff60405f205416036109d457805f52600b60205260ff60405f205460281c1660078110156103dd576005116109c55760c883116109b6576109ab7f0ff410db68b95933b3297828599ba335d9c49f904355784c6a2bbbe839541dbc946040519485948552606060208601526060850191614464565b9060408301520390a1005b6347e8b47560e11b5f5260045ffd5b63d52ab5d160e01b5f5260045ffd5b6325d7e13960e11b5f5260045ffd5b6109fc915060203d602011610897576108898183613f2e565b85610922565b346102b957610a1036613ec1565b60015460405163e743862960e01b8152336004820152602481018390529194929190602090829060449082906001600160a01b03165afa90811561089e575f91610beb575b501561086057835f52600b6020528164ffffffffff60405f205416036109d457835f52600b60205260ff60405f205460281c1660078110156103dd576002116109c557815f52600a60205260405f205460ff8160881c165f52600c60205261ffff610ac9838260405f209460501c16614192565b915460081c1610610bdc5763ffffffff82165f5b828110610b1d5750507fac65b29e315dbc954a8da88cc0a5b5c6a59d3223452c29b42b5db6494a4f7d2693610b1891604051948594856141e7565b0390a1005b610b2881848761419f565b35845f52600a602052610b7a60405f20825f526002810160205260405f2060ff198154169055610b5f61ffff825460501c166154d4565b61ffff60501b82549160501b169061ffff60501b1916179055565b5f52600b60205260405f20908464ffffffffff835460301c1603610bcd5781546affffffffffff00000000001984166affffffffffffffffffffff19909116176501000000000017909155600101610add565b63637df6ff60e11b5f5260045ffd5b632b229f6b60e11b5f5260045ffd5b610c04915060203d602011610897576108898183613f2e565b85610a55565b346102b957610c1836613e7b565b600354604051627eeac760e11b81523360048201526024810183905290602090829060449082906001600160a01b03165afa90811561089e575f91610cb9575b5015610caa57805f52600b6020528164ffffffffff60405f205416036109d457815f52600a6020526001600160401b0360405f205416610c9b576102e69161511b565b6315d7cf2960e31b5f5260045ffd5b63078eec1360e11b5f5260045ffd5b90506020813d602011610ce3575b81610cd460209383613f2e565b810103126102b9575183610c58565b3d9150610cc7565b346102b9575f3660031901126102b95760206001600160501b0360045460a01c16604051908152f35b346102b95760403660031901126102b9576004356001600160401b0381116102b957610d44903690600401613e91565b600354604051627eeac760e11b815233600482015260248035908201819052939291602090829060449082906001600160a01b03165afa90811561089e575f91610e58575b5015610caa578015610e4a575f5b818110610de357507f284330b7aeacaa076942744a635969b490cf5cafa294d5b6c311f387078afa2892610dd86040519384936040855260408501916141c3565b9060208301520390a1005b610dee81838561419f565b3590815f52600a602052600260405f2001855f5260205260ff60405f20541615610e3b576001915f52600a602052600260405f2001855f5260205260405f2060ff19815416905501610d97565b6362bfd17560e11b5f5260045ffd5b6254c4fb60e71b5f5260045ffd5b90506020813d602011610e82575b81610e7360209383613f2e565b810103126102b9575184610d89565b3d9150610e66565b346102b95760403660031901126102b9576004356001600160401b0381116102b957610eba903690600401613e91565b610ec2613e6c565b610eca614d7f565b151560ff165f5b828110610eda57005b806001600160a01b03610ef8610ef3600194878961419f565b614231565b165f52600f60205260405f208360ff1982541617905501610ed1565b346102b95760203660031901126102b9576004355f52600a602052602061ffff60405f205460981c16604051908152f35b346102b95760203660031901126102b9576004356001600160501b038116908181036102b9577f7934a8db3f8f75ea0c15b07050e7abbb51f58cfa3914f76450dc170f3d40d81191602091610f98614d7f565b6004805469ffffffffffffffffffff60a01b191660a09290921b69ffffffffffffffffffff60a01b16919091179055604051908152a1005b346102b95760603660031901126102b957602435600435610fef613e2b565b600354604051627eeac760e11b81523360048201526024810185905291929190602090829060449082906001600160a01b03165afa90811561089e575f91611140575b5015610caa57805f52600a60205260ff60405f205460881c165f52600c60205260405f205460ff81169081156108515760ff841691821115611131576110778261458c565b815f52600c6020526001600160501b038060405f205460681c169160681c169003936001600160501b03851161111d5761110a6060946110e06001600160501b037ff926d4ef55149e1fe1f7e953b8bddc590c485497a4f01d73ea589b8150bfed839816614be0565b5f858152600a60205260409020805460ff60881b191660889290921b60ff60881b16919091179055565b60405192835260208301526040820152a1005b634e487b7160e01b5f52601160045260245ffd5b63c59efceb60e01b5f5260045ffd5b90506020813d60201161116a575b8161115b60209383613f2e565b810103126102b9575184611032565b3d915061114e565b346102b9575f3660031901126102b9576111b9604051611193604082613f2e565b60058152640352e302e360dc1b6020820152604051918291602083526020830190613fff565b0390f35b346102b95760203660031901126102b95760206111ee6004355f52600b60205264ffffffffff60405f205416151590565b6040519015158152f35b613e3b565b346102b95760603660031901126102b957600435611219613e6c565b600354604051627eeac760e11b815233600482015260448035602483018190529492602091839182906001600160a01b03165afa90811561089e575f91611307575b5015610caa57825f52600b6020528064ffffffffff60405f205416036109d457825f52600b60205260ff60405f205460281c1660078110156103dd576002116109c5575f818152600a60205260409020805460ff60901b1916831560901b60ff60901b161790557fdef22600eb997387bdb5a7edea2add06b5e92c0fda852986932731166349355192610b18906040519384938460409194939260608201958252151560208201520152565b90506020813d602011611331575b8161132260209383613f2e565b810103126102b957518461125b565b3d9150611315565b346102b95760803660031901126102b95760443560043560243560078310156102b957600354604051627eeac760e11b8152336004820152606435602482018190529391602090829060449082906001600160a01b03165afa90811561089e575f916114ae575b5015610caa57815f52600b6020528064ffffffffff60405f205416036109d457815f52600b60205260ff60405f205460281c1693835f52600b60205260ff60405f205460281c166007811015806103dd57828211156114a0578486036114815760078710156103dd576103dd578511611472575b60078510156103dd578085146114635780851115611457578061143c57506102e69350614db2565b919060066102e695036150a257611452816154e7565b6150a2565b91906102e694506150a2565b635f5db10960e11b5f5260045ffd5b63bea1b71360e01b5f5260045ffd5b5060078610156103dd57851061141457631b1613f760e01b5f5260045ffd5b624da49d60e71b5f5260045ffd5b90506020813d6020116114d8575b816114c960209383613f2e565b810103126102b95751856113a0565b3d91506114bc565b346102b9576114ee36613e7b565b600354604051627eeac760e11b81523360048201526024810183905290602090829060449082906001600160a01b03165afa90811561089e575f91611583575b5015610caa577f86ef48d3478dfc87f7a61b90ffdd3193d9862a68632a4c233e71d330b3bb5ce591816040925f52600b602052825f206affffffffff00000000000019815416905582519182526020820152a1005b90506020813d6020116115ad575b8161159e60209383613f2e565b810103126102b957518361152e565b3d9150611591565b346102b9576115c336614023565b60015460405163e743862960e01b81523360048201526024810184905291939190602090829060449082906001600160a01b03165afa90811561089e575f91611716575b501561086057805f52600a60205260405f20916002830193815f528460205260ff60405f20541615610e3b5761164f825f52600b60205264ffffffffff60405f205416151590565b6108335761165d908361563b565b825460ff8160881c165f52600c60205261ffff8060405f205460081c169160501c161015610bdc576116cf6040937f4029968a3f2d34f7c8df00f90946159de86b935a5c57f786888ce5d1d7845d9c95835f52602052845f2060ff198154169055610b5f61ffff825460501c166154d4565b5f818152600b60209081529084902080546affffffffff0000000000001965ffffffffffff1990911663ffffffff86161765010000000000171690558351928352820152a1005b61172f915060203d602011610897576108898183613f2e565b84611607565b346102b9575f3660031901126102b95760206001600160a01b035f5160206158465f395f51905f525416604051908152f35b346102b95761177536613fa0565b9061177e614d7f565b5f5b8281106117b8576040517feda6ff97e5243629083aff63c9a0f9a5ad50c6d235c74b51e9ec92dc6ad5f6db9080610b188686836142db565b60ff6117cd6117c88386866142bd565b6142cd565b16151580611806575b156117f757806117f16117ec60019386866142bd565b615288565b01611780565b632520e1bd60e11b5f5260045ffd5b5060ff6118176117c88386866142bd565b165f52600c60205260ff60405f205416156117d6565b346102b95761183b36613ec1565b600354604051627eeac760e11b81523360048201526024810183905291929190602090829060449082906001600160a01b03165afa90811561089e575f91611958575b5015610caa57815f52600b6020528364ffffffffff60405f205416036109d457815f52600b60205260ff60405f205460281c1660078110156103dd576002116109c5578015611949575f5b81811061190457507f5ceebf1e578529395e97c10d2075cccca96caf69041edf3d24f9a95bf42b69689391610b1891604051948594856141e7565b61190f81838661419f565b355f52600b60205260405f20908564ffffffffff835460301c1603610bcd5781546affffffffff00000000000019169091556001016118c9565b63d411ab5760e01b5f5260045ffd5b90506020813d602011611982575b8161197360209383613f2e565b810103126102b957518561187e565b3d9150611966565b346102b95760203660031901126102b9576004355f52600b60205264ffffffffff60405f2054165f52600a6020526111b96119ca600160405f20016143c6565b604051918291602083526020830190613fff565b346102b95760803660031901126102b9576004356024356001600160401b0381116102b957611a11903690600401613e91565b916044356001600160401b0381116102b957611a31903690600401613e91565b600354604051627eeac760e11b8152336004820152606435602482018190529396929591602090829060449082906001600160a01b03165afa90811561089e575f91611c14575b5015610caa575f5b828110611a8957005b611a9481848461419f565b3590611aa181888a61419f565b359160078310156102b957600354604051627eeac760e11b81523360048201526024810188905290602090829060449082906001600160a01b03165afa90811561089e575f91611be3575b5015610caa57805f52600b6020528664ffffffffff60405f205416036109d457805f52600b60205260ff60405f205460281c16865f52600b60205260ff60405f205460281c166007811015806103dd57858211156114a057838903611bc45760078310156103dd576103dd578111611472575b60078110156103dd5783811461146357600193879181811115611bb95781611b93575050611b8d9188614db2565b01611a80565b926006611ba69414611bab575b896150a2565b611b8d565b611bb48a6154e7565b611ba0565b50611ba692896150a2565b5060078210156103dd578110611b5f57631b1613f760e01b5f5260045ffd5b90506020813d8211611c0c575b81611bfd60209383613f2e565b810103126102b957518a611aec565b3d9150611bf0565b90506020813d602011611c3e575b81611c2f60209383613f2e565b810103126102b9575187611a78565b3d9150611c22565b346102b9575f3660031901126102b957611c5e614d7f565b5f6001600160a01b035f5160206158465f395f51905f52546001600160601b0360a01b81165f5160206158465f395f51905f5255167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346102b95760203660031901126102b9576004356001600160401b0381116102b957611ce9903690600401613e91565b90611cf2614d7f565b5f5b828110611cfd57005b80611d2c611d0e600193868661419f565b358060081c5f526011602052600160ff60405f2092161b8154179055565b01611cf4565b346102b95760203660031901126102b9576004356001600160401b0381116102b9576111ee611d7461047b611d6d6020943690600401613dfe565b3691613f6a565b615497565b346102b9576020611d92611d8c36613e7b565b90614393565b611d9f6040518092613ff2565bf35b346102b9575f3660031901126102b9576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163003611e0b5760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b63703e46dd60e11b5f5260045ffd5b346102b95760203660031901126102b95760043561ffff8116908181036102b9577fbb4df7a4ea901e618bc10245eee20e4c8522555bfe10816b8128332bc0e334a991602091611e68614d7f565b6003805461ffff60c81b191660c89290921b61ffff60c81b16919091179055604051908152a1005b346102b957611e9e36613fa0565b611ea6614d7f565b5f5b818110611ee157507fab0ba102ddeb1d896e289b4c68719d8a1f4a198be3ab6883484a1fe929718a4f91610b18604051928392836142db565b80611efd60ff611ef76117c860019587896142bd565b1661458c565b611f0b6117ec8285876142bd565b01611ea8565b60403660031901126102b957611f25613dd9565b6024356001600160401b0381116102b957366023820112156102b957611f55903690602481600401359101613f6a565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001680301490811561211a575b50611e0b57611f97614d7f565b6040516352d1902d60e01b81526001600160a01b0383169290602081600481875afa5f91816120e6575b50611fd95783634c9c8ce360e01b5f5260045260245ffd5b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8592036120d45750813b156120c2577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b031916821790557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a28151156120aa575f808360206102e695519101845af43d156120a2573d9161208683613f4f565b926120946040519485613f2e565b83523d5f602085013e6157eb565b6060916157eb565b5050346120b357005b63b398979f60e01b5f5260045ffd5b634c9c8ce360e01b5f5260045260245ffd5b632a87526960e21b5f5260045260245ffd5b9091506020813d602011612112575b8161210260209383613f2e565b810103126102b957519085611fc1565b3d91506120f5565b90506001600160a01b037f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416141583611f8a565b346102b95760203660031901126102b9575f60a060405161216f81613ef8565b82815282602082015282604082015282606082015282608082015201526004355f52600c60205260c060405f206001600160501b03604051916121b183613ef8565b5464ffffffffff60ff82169384815262ffffff6020820161ffff8560081c16815261ffff60408401818760181c168152816060860193858960281c1685528960a060808901988a8c60401c168a52019960681c1689526040519a8b52511660208a0152511660408801525116606086015251166080840152511660a0820152f35b346102b957602061224236613e7b565b5f52600b825264ffffffffff60405f20541614604051908152f35b346102b95760403660031901126102b95760243561ffff811681036102b9576001600160a01b036008541633036122bd576004355f908152600a60205260409020805461ffff60981b191660989290921b61ffff60981b16919091179055005b63993eecd960e01b5f5260045ffd5b346102b95760203660031901126102b9576004355f52600b60205264ffffffffff60405f2054165f52600a602052602060ff60405f205460881c16604051908152f35b346102b95761231d36613ec1565b600354604051627eeac760e11b81523360048201526024810183905292949290602090829060449082906001600160a01b03165afa90811561089e575f91612433575b5015610caa57805f52600b6020528164ffffffffff60405f205416036109d457805f52600b60205260ff60405f205460281c1660078110156103dd576002116109c557815f52600a60205260405f20938015610e4a5760025f9501945b8181106123f7576040517f473f1c3e2a696b3007a52e476d439236942f08e0cff330fc24aeefab41d0d98e9080610b1886868a8a856141e7565b61240281838761419f565b3590815f528660205260ff60405f20541615610e3b576001915f528660205260405f2060ff198154169055016123bd565b90506020813d60201161245d575b8161244e60209383613f2e565b810103126102b9575185612360565b3d9150612441565b346102b95760603660031901126102b9576024356004356001600160401b0382116102b957366023830112156102b9578160040135906001600160401b0382116102b9576024830192602436918460061b0101116102b95760015460405163e743862960e01b815233600482015260448035602483018190529392602091839182906001600160a01b03165afa90811561089e575f9161288a575b501561086057815f52600b6020528064ffffffffff60405f205416036109d457815f52600b60205260ff60405f205460281c1660078110156103dd576005116109c5576005831161287b576125548361420a565b926125626040519485613f2e565b80845261256e8161420a565b602085019590601f19013687376001600160a01b03600654165f5b8381106126ee575050825f52600a602052600360405f2001906801000000000000000083116126da5781548383558084106126b4575b50905f5260205f205f915b8383106126485750505050604051926060840191845260606020850152518091526080830193905f5b818110612629577f72efcd8f70847517ad3bc1486f78c9e8353718305008d04c90e86e8eacea2bdc8580888760408301520390a1005b82516001600160a01b03168652602095860195909201916001016125f3565b60016040826001600160a01b0361265f8495614231565b86546001600160a01b031916911617855561267c60208201614245565b855469ffffffffffffffffffff60a01b191660a09190911b69ffffffffffffffffffff60a01b161785559290940193920191016125ca565b825f528360205f2091820191015b8181106126cf57506125bf565b5f81556001016126c2565b634e487b7160e01b5f52604160045260245ffd5b6126fc610ef3828686614221565b906001600160a01b0360405192633af32abf60e01b84521691826004820152602081602481875afa90811561089e575f9161285d575b501561284e576001600160501b036127566020612750848989614221565b01614245565b166102d181036127da57506040516301ffc9a760e01b81526380ac58cd60e01b6004820152602081602481865afa90811561089e575f916127bc575b50156127ad576001915b6127a6828a614296565b5201612589565b63adc1f5ed60e01b5f5260045ffd5b6127d4915060203d8111610897576108898183613f2e565b8a612792565b6104830361283f576040516301ffc9a760e01b8152636cdb3d1360e11b6004820152602081602481865afa90811561089e575f91612821575b50156127ad5760019161279c565b612839915060203d8111610897576108898183613f2e565b8a612813565b630c1516e360e31b5f5260045ffd5b63f8f94f4960e01b5f5260045ffd5b612875915060203d8111610897576108898183613f2e565b8a612732565b636e58b71560e01b5f5260045ffd5b6128a3915060203d602011610897576108898183613f2e565b85612500565b346102b9576128b736613ec1565b600354604051627eeac760e11b81523360048201526024810183905291929190602090829060449082906001600160a01b03165afa90811561089e575f91612a10575b5015610caa57815f52600b6020528364ffffffffff60405f205416036109d457815f52600b60205260ff60405f205460281c1660078110156103dd576002116109c557835f52600a60205260405f205460ff8160881c165f52600c60205261ffff61296e838260405f209460501c16614192565b915460081c1610610bdc575f5b8181106129b657507f5b1a562619687202d271aec5b478cdde4e6e135bbdec7a5f7a11bf3056b534269391610b1891604051948594856141e7565b6129c181838661419f565b3590855f52600a602052600260405f2001825f528060205260ff60405f205416612a01576001925f5260205260405f208260ff198254161790550161297b565b63cbc57df560e01b5f5260045ffd5b90506020813d602011612a3a575b81612a2b60209383613f2e565b810103126102b95751856128fa565b3d9150612a1e565b346102b957612a5036613e7b565b90815f52600b60205264ffffffffff60405f2054161480612a79575b6020906040519015158152f35b505f52600b60205260ff60405f205460281c1660078110156103dd5760046020911015612a6c565b346102b9576101803660031901126102b957612abb613dd9565b6064356001600160401b0381116102b957612ada903690600401613dfe565b91906084356001600160401b0381116102b957612afb903690600401613dfe565b60a4949194356001600160401b0381116102b957612b1d903690600401613dfe565b60c4969196356001600160401b0381116102b957612b3f903690600401613dfe565b939092610164359687151588036102b9576001600160a01b03601254163303612f0b576044355f908152600a60205260409020805471ff000000000000000000ffffffffffffffff19166024356001600160401b0316176101243560881b60ff60881b1617815596875461ffff60401b191660e43560401b61ffff60401b16178855875466ffffffffffffff60501b19166101043560601b64ffffffffff60601b1617600160501b178855875461ffff60981b19166101443560981b61ffff60981b161788556024355f908152600b60205260409020805465ffffffffffff191660443563ffffffff1617650600000000001790556001600160401b0381116126da57612c5c81612c5360018b01546140e0565b60018b0161412e565b5f601f8211600114612e645795899791955f9c7fbd0ab160e05d3247ed89c8619b640c22634a65d36949c146e3133fc319ccfb4d96612d1e968f60209f9d9b97612db89f9b8d6001612cca8380612cfb9e612cf69891612e59575b508160011b915f199060031b1c19161790565b9101555b612ce4612cdf61047b368486613f6a565b614173565b805460ff191660011790553691613f6a565b614b40565b60405191829160443583526024358b84015260c0604084015260c0830190614055565b60e43560608301526101243560808301526101043560a08301520390a1612df0575b50507fd0f236da2a55211dd4cf5df9ab238a0858e8d127f01ce8d39d4299657e6d10c86040805160443581526101443586820152a16001600160a01b036002541690604051948580948193638471af7760e01b83526044359060048401602090939291936001600160a01b0360408201951681520152565b03925af1801561089e57612dc857005b6102e69060203d602011612de9575b612de18183613f2e565b8101906140ad565b503d612dd7565b805460ff60901b191682151560901b60ff60901b161790557fdef22600eb997387bdb5a7edea2add06b5e92c0fda852986932731166349355190612e4f9060408051604435815291156020830152602435908201529081906060820190565b0390a18480612d40565b90508601355f612cb7565b600189015f5260205f20905f5b601f1984168110612ef35750955f9c7fbd0ab160e05d3247ed89c8619b640c22634a65d36949c146e3133fc319ccfb4d96612d1e96612cf660209f9d9b9597612db89f9d97612cfb998f9d82601f19811610612eda575b50508d60018083811b01910155612cce565b8301355f19600384901b60f8161c191690555f80612ec8565b90916020600181928588013581550193019101612e71565b637fea9dc560e01b5f5260045ffd5b346102b95760203660031901126102b9576004355f52600a60205260ff60405f205460881c165f52600c602052602061ffff60405f205460181c16604051908152f35b346102b95760a03660031901126102b9576004356001600160a01b0381168091036102b9576024356001600160a01b0381168091036102b957612f9e613dad565b91612fa7613dc3565b90608435936001600160a01b0385168095036102b957612fc5614d7f565b600254936001600160a01b038516818115918215613061575b505015613052576001600160a01b039485936001600160601b0360a01b60015416176001556001600160601b0360a01b1617600255166001600160601b0360a01b6007541617600755166001600160601b0360a01b60085416176008556001600160601b0360a01b60095416176009555f80f35b6337b8bea960e21b5f5260045ffd5b1490508188612fde565b346102b95760e03660031901126102b9576004356024356001600160401b0381116102b95761309e903690600401613dfe565b90916044356001600160401b0381116102b9576130bf903690600401613dfe565b9290936064356001600160401b0381116102b9576130e1903690600401613dfe565b6084939193356001600160401b0381116102b957613103903690600401613dfe565b60015460405163e743862960e01b815233600482015260c435602482018190529960a4359893959392909190602090829060449082906001600160a01b03165afa90811561089e575f91613250575b501561086057895f52600b6020528864ffffffffff60405f205416036109d457895f52600b60205260ff60405f205460281c1660078110156103dd576005116109c5577fff9c4aa2c485437fd53cb8c1462dd050bd76f4e240718699dd1bb1bb248c6be49a6131f861320b97613229998c5f52600a60205260ff60405f205460881c165f52600c6020526131f262ffffff60405f205460281c168d6145b1565b8c6145df565b613234575b612cf6878787878787614968565b60405194859485526020850152608060408501526080840190614055565b9060608301520390a1005b61324b6001600160501b0360045460a01c16614be0565b6131fd565b613269915060203d602011610897576108898183613f2e565b8c613152565b346102b95760603660031901126102b95760443560243560043560078310156102b9575f818152600a6020908152604091829020546003549251627eeac760e11b81523360048201526001600160401b03909116602482015291829060449082906001600160a01b03165afa90811561089e575f91613377575b5015610caa57815f52600b6020528064ffffffffff60405f205416036109d457805f52600a6020526001600160401b0360405f2054169283831461336857801561335957600681101561334a5783613345916102e695846150a2565b61511b565b63cbcd7cd360e01b5f5260045ffd5b50613345836102e69483614db2565b633493d67f60e11b5f5260045ffd5b90506020813d6020116133a1575b8161339260209383613f2e565b810103126102b95751846132e9565b3d9150613385565b346102b95760603660031901126102b9576004356024359064ffffffffff82168092036102b957604435908115158092036102b957335f52600f60205260ff60405f2054161561347257805f52600a60205260405f20918254938064ffffffffff8660a81c16019364ffffffffff851161111d577ff34537c7169eedc3a5bf2996afbe8ee8754382962b6a4276ee7d450e49748a0f9560609564ffffffffff60a81b9060a81b169064ffffffffff60a81b191617905560405192835260208301526040820152a1005b63e5e9973760e01b5f5260045ffd5b346102b95760603660031901126102b95760043560ff8116908181036102b9576024359160ff8316928381036102b9576134b9613e2b565b6134c1614d7f565b606460ff6134d8836134d386896140cc565b6140cc565b160361354a577fb4ae64ee51c0d84410a16847b3f8f3d7ac7bbbeaf2abae8671beba816f0279e39460609460ff936005548560b01b8560b01b16928660a01b9060a01b169062ffffff60a01b191617908560a81b9060a81b1617176005556040519384526020840152166040820152a1005b63196f075b60e11b5f5260045ffd5b346102b95760e03660031901126102b9576024356001600160401b0381116102b957613589903690600401613dfe565b906044356001600160401b0381116102b9576135a9903690600401613dfe565b9190926064356001600160401b0381116102b9576135cb903690600401613dfe565b94906084356001600160401b0381116102b9576135ec903690600401613dfe565b96909160a4359661ffff8816948589036102b95760c4359660ff881688036102b95760015460405163e743862960e01b81523360048083019190915235602482015290602090829060449082906001600160a01b03165afa90811561089e575f91613a11575b5015610860576136766004355f52600b60205264ffffffffff60405f205416151590565b6108335760ff88165f52600c60205260405f209861369660ff8a1661458c565b6136a962ffffff8b5460281c16896145b1565b6003549a64ffffffffff808d60a01c161461111d5764ffffffffff60a01b198c1660a08d811c64ffffffffff166001810190911b64ffffffffff60a01b16919091176003555f908152600a60205260409020805471ff000000000000000000ffffffffffffffff19166004356001600160401b03161760888c901b60ff60881b161781556137969161375190825461ffff60401b191660409190911b61ffff60401b16178255565b805466ffffffffffffff60501b19164260601b64ffffffffff60601b1617600160501b178155600354815461ffff60981b191660309190911c61ffff60981b16179055565b6004355f908152600b60205260409020805465ffffffffffff191660a08d901c63ffffffff161765060000000000179081905560301c64ffffffffff16806138c9575b50613845612db89c6001600160501b039864ffffffffff9c987fbd0ab160e05d3247ed89c8619b640c22634a65d36949c146e3133fc319ccfb4d9860209f9d97988f998f61388b9f9960ff9961386c9d6138369360a01c166145df565b50612cf6878787878787614968565b926040519485948c8c60a01c1686526004358e87015260c0604087015260c0860190614055565b9260608501521660808301524260a08301520390a15460681c16614be0565b600254604051638471af7760e01b815233600482015260a09290921c90921660248201529283916001600160a01b03169082905f9082906044820190565b600354604051627eeac760e11b815233600480830191909152356024820152989c979b979a9699959894969395939490602090829060449082906001600160a01b03165afa90811561089e575f916139df575b5015610caa57612db89c60209c64ffffffffff9c61388b9b6001600160501b039b8f9a8f61386c9b60ff997fbd0ab160e05d3247ed89c8619b640c22634a65d36949c146e3133fc319ccfb4d9e613836937f86ef48d3478dfc87f7a61b90ffdd3193d9862a68632a4c233e71d330b3bb5ce560406138459d6004355f526020600b9052815f206affffffffff00000000000019815416905581519081526004356020820152a150509950999f5050989e9f5098509850989c9d5098509c506137d9565b90506020813d602011613a09575b816139fa60209383613f2e565b810103126102b957518e61391c565b3d91506139ed565b613a2a915060203d602011610897576108898183613f2e565b8c613652565b346102b9576101203660031901126102b9576004356001600160a01b0381168091036102b957602435906001600160a01b0382168092036102b957613a73613dad565b90613a7c613dc3565b92608435906001600160501b038216938483036102b95760a4356001600160a01b0381168091036102b95760c4359261ffff8416938481036102b95760e4359664ffffffffff881688036102b95761010435986001600160a01b038a16809a036102b9575f5160206158665f395f51905f52549060ff8260401c16159b8c6001600160401b03841680159182613da5575b506001149081613d9b575b159081613d92575b50613d83577fbb4df7a4ea901e618bc10245eee20e4c8522555bfe10816b8128332bc0e334a99a60209a7f7934a8db3f8f75ea0c15b07050e7abbb51f58cfa3914f76450dc170f3d40d811958f958d9660016001600160401b03198316175f5160206158665f395f51905f5255613d57575b50613b9b61555f565b613ba361555f565b613bac3361450e565b613bb461555f565b6001600160601b0360a01b5f5416175f556001600160a01b036003549a16916001600160a01b0360045494846001600160601b0360a01b871617600455166001600160601b0360a01b600554161760055564ffffffffff60a01b9060a01b1698898966ffffffffffffff60c81b8d1617176003556001600160601b0360a01b6006541617600655613c43614d7f565b7fffff0000000000000000000000000000000000000000000000000000000000009092161760a09190911b69ffffffffffffffffffff60a01b1617600455604051908152a1613c90614d7f565b7fffffffffff000000000000000000000000000000000000000000000000000000909316171760c89190911b61ffff60c81b1617600355604051908152a16010805468ffffffffffffffffff1916630668a004179055601280546001600160a01b031916919091179055613d0057005b68ff0000000000000000195f5160206158665f395f51905f5254165f5160206158665f395f51905f52557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a1005b68ffffffffffffffffff191668010000000000000001175f5160206158665f395f51905f52555f613b92565b63f92ee8a960e01b5f5260045ffd5b9050158e613b20565b303b159150613b18565b91508e613b0d565b604435906001600160a01b03821682036102b957565b606435906001600160a01b03821682036102b957565b600435906001600160a01b03821682036102b957565b359061ffff821682036102b957565b9181601f840112156102b9578235916001600160401b0383116102b957602083818601950101116102b957565b6044359060ff821682036102b957565b346102b95760203660031901126102b9576004355f52600b602052602064ffffffffff60405f205416604051908152f35b6024359081151582036102b957565b60409060031901126102b9576004359060243590565b9181601f840112156102b9578235916001600160401b0383116102b9576020808501948460051b0101116102b957565b9060606003198301126102b95760043591602435906001600160401b0382116102b957613ef091600401613e91565b909160443590565b60c081019081106001600160401b038211176126da57604052565b606081019081106001600160401b038211176126da57604052565b90601f801991011681019081106001600160401b038211176126da57604052565b6001600160401b0381116126da57601f01601f191660200190565b929192613f7682613f4f565b91613f846040519384613f2e565b8294818452818301116102b9578281602093845f960137010152565b9060206003198301126102b9576004356001600160401b0381116102b957826023820112156102b9578060040135926001600160401b0384116102b957602460c08502830101116102b9576024019190565b9060078210156103dd5752565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b60609060031901126102b957600435906024359060443590565b908160209103126102b9575180151581036102b95790565b9080602083519182815201916020808360051b8301019401925f915b83831061408057505050505090565b909192939460208061409e600193601f198682030187528951613fff565b97019301930191939290614071565b908160209103126102b957516001600160a01b03811681036102b95790565b9060ff8091169116019060ff821161111d57565b90600182811c9216801561410e575b60208310146140fa57565b634e487b7160e01b5f52602260045260245ffd5b91607f16916140ef565b818110614123575050565b5f8155600101614118565b9190601f811161413d57505050565b614167925f5260205f20906020601f840160051c83019310614169575b601f0160051c0190614118565b565b909150819061415a565b60208091604051928184925191829101835e8101600d81520301902090565b9190820180921161111d57565b91908110156141af5760051b0190565b634e487b7160e01b5f52603260045260245ffd5b81835290916001600160fb1b0383116102b95760209260051b809284830137010190565b949392916040926142059287526060602088015260608701916141c3565b930152565b6001600160401b0381116126da5760051b60200190565b91908110156141af5760061b0190565b356001600160a01b03811681036102b95790565b356001600160501b03811681036102b95790565b8051156141af5760200190565b8051600110156141af5760400190565b8051600210156141af5760600190565b8051600310156141af5760800190565b80518210156141af5760209160051b010190565b8181029291811591840414171561111d57565b91908110156141af5760c0020190565b3560ff811681036102b95790565b60208082528101839052604001915f905b8082106142f95750505090565b909192833560ff81168091036102b957815261ffff61431a60208601613def565b16602082015261ffff61432f60408601613def565b166040820152606084013562ffffff81168091036102b9576060820152608084013564ffffffffff81168091036102b957608082015260a0840135906001600160501b0382168092036102b95760c08160019360a0839401520194019201906142ec565b815f52600b60205264ffffffffff60405f205416146143b157505f90565b5f52600b60205260ff60405f205460281c1690565b9060405191825f8254926143d9846140e0565b808452936001811690811561444257506001146143fe575b5061416792500383613f2e565b90505f9291925260205f20905f915b818310614426575050906020614167928201015f6143f1565b602091935080600191548385890101520191019091849261440d565b90506020925061416794915060ff191682840152151560051b8201015f6143f1565b908060209392818452848401375f828201840152601f01601f1916010190565b908154916144918361420a565b9261449f6040519485613f2e565b80845260208401915f5260205f20915f905b8282106144be5750505050565b604051604081018181106001600160401b038211176126da5760019283926020926040526001600160501b0388546001600160a01b038116835260a01c16838201528152019401910190926144b1565b6001600160a01b03168015614579576001600160a01b035f5160206158465f395f51905f5254826001600160601b0360a01b8216175f5160206158465f395f51905f5255167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3565b631e4fbdf760e01b5f525f60045260245ffd5b5f52600c60205260ff60405f205416156145a257565b630bec67bf60e31b5f5260045ffd5b9081151591826145d4575b5050156145c557565b6305615c0b60e11b5f5260045ffd5b111590505f806145bc565b9192905f6146209173e3223eaf0e260b54a8ce777ac9f4a972310370c095604051938492839263b2dad15560e01b8452602060048501526024840191614464565b0381875af490811561089e575f916148ef575b50809360038251106148e05760148251116148d157602060405180926391ae2cdb60e01b8252826004830152818061466e6024820188613fff565b03915af490811561089e575f916148b2575b50156148a35761468f816151ba565b925f52600a602052600160405f2001906146ab61047b836143c6565b9360405194602086018151966146db6020828186019a808c875e81015f838201520301601f198101835282613f2e565b51902091604051926020840182519461470e60208281870198808a875e81015f838201520301601f198101835282613f2e565b51902014159687614722575b505050505050565b61472b82615497565b61487d5760ff60405160208185518089835e8101600d815203019020541661486e5760209251908161484a575b5050604051928391518091835e8101600d815203019020600160ff198254161790558051906001600160401b0382116126da5761479f8261479985546140e0565b8561412e565b602090601f83116001146147e7576147ce92915f91836147dc575b50508160011b915f199060031b1c19161790565b90555b5f808080808061471a565b015190505f806147ba565b90601f19831691845f52815f20925f5b818110614832575090846001959493921061481a575b505050811b0190556147d1565b01515f1960f88460031b161c191690555f808061480d565b929360206001819287860151815501950193016147f7565b839082604051938492835e8101600d81520301902060ff1981541690555f80614758565b633480a50360e21b5f5260045ffd5b604051633b4c9c8960e21b8152602060048201528061489f6024820185613fff565b0390fd5b634caa0cad60e11b5f5260045ffd5b6148cb915060203d602011610897576108898183613f2e565b5f614680565b63680b6caf60e01b5f5260045ffd5b6365ac5e6760e11b5f5260045ffd5b90503d805f833e6149008183613f2e565b8101906020818303126102b9578051906001600160401b0382116102b9570181601f820112156102b95780519061493682613f4f565b926149446040519485613f2e565b828452602083830101116102b957815f9260208093018386015e830101525f614633565b949392909460198611614b315785158015614b26575b15614b175760206149c19173e3223eaf0e260b54a8ce777ac9f4a972310370c09760405193849283926309f241ed60e41b84528560048501526024840191614464565b0381895af490811561089e575f91614af8575b5015614ae95760198111614ada57614a0b9160209160405193849283926309f241ed60e41b84528560048501526024840191614464565b0381875af490811561089e575f91614abb575b5015614aad5760198211614a9e57614a56926020926040518095819482936309f241ed60e41b84528760048501526024840191614464565b03915af490811561089e575f91614a7f575b5015614a7057565b6388d04d1f60e01b5f5260045ffd5b614a98915060203d602011610897576108898183613f2e565b5f614a68565b6348997f7160e01b5f5260045ffd5b620f7f1b60e91b5f5260045ffd5b614ad4915060203d602011610897576108898183613f2e565b5f614a1e565b6304121ae360e31b5f5260045ffd5b63088cc68560e21b5f5260045ffd5b614b11915060203d602011610897576108898183613f2e565b5f6149d4565b638c72c2a160e01b5f5260045ffd5b50600486101561497e565b630da1592560e21b5f5260045ffd5b9695929194939060405195614b5660a088613f2e565b6004875260805f5b818110614bcf57505092614bbd9492614b97614baa93899896614bcc9a9c614b858b614259565b52614b8f8a614259565b503691613f6a565b614ba088614266565b52614b8f87614266565b614bb385614276565b52614b8f84614276565b614bc682614286565b52614286565b50565b806060602080938c01015201614b5e565b5f906001600160a01b035f54166064614c0160ff60055460a01c16846142aa565b04813b156102b95760405163079cc67960e41b815233600482015260248101919091525f8160448183865af1801561089e57614d6a575b50614c9360206001600160a01b03600454166064614c5e60ff60055460a81c16876142aa565b6040516323b872dd60e01b81523360048201526001600160a01b03909316602484015204604482015291829081906064820190565b038187865af18015614d5f5791849391602093614d0d9650614d44575b506001600160a01b03846064614cd06005549560ff8760b01c16906142aa565b0493604051978895869485936323b872dd60e01b85521633600485016001600160a01b036040929594938160608401971683521660208201520152565b03925af1908115614d385750614d205750565b614bcc9060203d602011610897576108898183613f2e565b604051903d90823e3d90fd5b614d5a90843d8611610897576108898183613f2e565b614cb0565b6040513d86823e3d90fd5b614d779193505f90613f2e565b5f915f614c38565b6001600160a01b035f5160206158465f395f51905f5254163303614d9f57565b63118cdaa760e01b5f523360045260245ffd5b905f92825f52600a60205260405f20826001600160401b0382541614615094575b61ffff815460501c1690811561111d5780546bffff0000000000000000000019165f1990920160501b61ffff60501b1691909117815561ffff905460501c166150585750815f52600a60205260405f2061ffff815460501c1661504957612cdf61047b6001614e4293016143c6565b60ff198154169055815f52600a602052600360405f205f815560018101614e6981546140e0565b9081615006575b5050018054905f815581614fe8575b50507fe5cdc676962c829e36cbb7b0c60a322b60e2d01db199590aab156803c5a97ae66020604051848152a15b5f818152600b60205260409020805465ffffffffffff191690556007546001600160a01b0316803b156102b9575f8091604460405180948193637792156760e11b83528860048401528760248401525af1801561089e57614fd3575b506001600160a01b0360085416803b15614fb657838091604460405180948193637792156760e11b83528860048401528760248401525af18015614d5f57908491614fba575b50506001600160a01b036009541691823b15614fb6579060448492836040519586948593637792156760e11b8552600485015260248401525af18015614fab57614f96575050565b614fa1828092613f2e565b614fa85750565b80fd5b6040513d84823e3d90fd5b8380fd5b81614fc491613f2e565b614fcf57825f614f4e565b8280fd5b614fe09193505f90613f2e565b5f915f614f08565b5f5260205f20908101905b81811015614e7f575f8155600101614ff3565b81601f5f931160011461501d5750555b5f80614e70565b8183526020832061503991601f0160051c810190600101614118565b8082528160208120915555615016565b6370141ec960e11b5f5260045ffd5b60607fa21e607ac5445b7603f2e7e913bb2a66f680c289fb1a2ce735eb6c92713c5dd991604051908582528460208301526040820152a1614eac565b61509d846154e7565b614dd3565b909192825f52600b60205260405f2060078510156103dd577f57ff30d4395dd2630a292b8a50feba238650ef9136dd5961908b899bb1ba99169460809482615113935465ff00000000008460281b169065ff0000000000191617905560405194855260208501526040840190613ff2565b6060820152a1565b5f818152600a60209081526040808320805467ffffffffffffffff19166001600160401b038716179055600e8252808320805464ffffffffff19169055848352600b825291829020805465ff000000000019166506000000000017905581519283528201929092527fb36e6603e4ebbca03e98d06ae8a134eb0c59baf95557ee9403a9fe073b6ac0749190a1565b9081518110156141af570160200190565b906151e8602080936040519481869251918291018484015e81015f838201520301601f198101845283613f2e565b5f5b8251811015614bcc576151fd81846151a9565b51604160f81b6001600160f81b0319821690811015908161525a575b50615228575b506001016151ea565b60f81c6020019060ff821161111d5760019160f81b6001600160f81b0319165f1a61525382866151a9565b539061521f565b602d60f91b101590505f615219565b3561ffff811681036102b95790565b3562ffffff811681036102b95790565b60ff615293826142cd565b1660018111615376575b5f52600c60205260405f209060ff6152b4826142cd565b1660ff198354161782556152ca60208201615269565b9082549064ffff0000006152e060408301615269565b60181b169167ffffff00000000006152fa60608401615278565b60281b169060808301359364ffffffffff851685036102b9576cffffffffff00000000000000009369ffffffffffffffffffff60681b9061533d9060a001614245565b60681b16956001600160501b0360681b199262ffff0086199260081b169067ffffffffffffff00191617161716179160401b1617179055565b5f19810181811161111d575f52600c60205260405f206040519061539982613ef8565b549060ff821681526020810161ffff8360081c16815260a0604083019261ffff8560181c1684526001600160501b03606082019562ffffff8160281c16875264ffffffffff8160401c16608084015260681c1691829101526001600160501b0361540560a08801614245565b16106154885761ffff8061541b60208801615269565b9251169116106154795761ffff8061543560408701615269565b92511691161061546a5762ffffff8061545060608601615278565b9251169116101561529d57633f07a64560e01b5f5260045ffd5b6383202d3d60e01b5f5260045ffd5b637dc1f31b60e01b5f5260045ffd5b636dddf41160e11b5f5260045ffd5b6154d1906040516154c76020828180820195805191829101875e81015f838201520301601f198101835282613f2e565b51902060106155a8565b90565b61ffff1661ffff811461111d5760010190565b5f818152600a60209081526040808320805467ffffffffffffffff198116909155600e835292819020805464ffffffffff19164264ffffffffff1617905580519384526001600160401b0392909216908301527f81ec2aca54fed63234990c222e20c265fbd80545574659c8e359fe9dc3cda16291a1565b60ff5f5160206158665f395f51905f525460401c161561557b57565b631afcd79f60e31b5f5260045ffd5b8115615594570690565b634e487b7160e01b5f52601260045260245ffd5b805460ff8116939290841561055d5760081c6001600160401b0316905f5b8560ff8216106155da575060019450505050565b600160ff61560e85604051602081019061052181610513898b869091602192825260ff60f81b9060f81b1660208201520190565b8060081c5f52828701602052161b60405f205416156156325760010160ff166155c6565b505f9450505050565b9190915f52600a602052615654600360405f2001614484565b91825161566057509050565b5f90815b84518310156157d0576104836001600160501b0360206156848689614296565b51015116036157245780156156a0575b600190925b0191615664565b506001600160a01b036156b38386614296565b5151604051627eeac760e11b8152336004820152602481018490529160209183916044918391165afa801561089e575f906156f2575b15159050615694565b506020813d821161571c575b8161570b60209383613f2e565b810103126102b957600190516156e9565b3d91506156fe565b916102d16001600160501b03602061573c8489614296565b510151161461574e575b600190615699565b91801561575c575b91615746565b50602460206001600160a01b036157738588614296565b515116604051928380926331a9108f60e11b82528660048301525afa801561089e576001915f916157b2575b506001600160a01b031633149050615756565b6157ca915060203d8111612de957612de18183613f2e565b5f61579f565b9293505050156157dc57565b63466e5a3d60e11b5f5260045ffd5b9061580f575080511561580057805190602001fd5b63d6bda27560e01b5f5260045ffd5b8151158061583c575b615820575090565b6001600160a01b0390639996b31560e01b5f521660045260245ffd5b50803b1561581856fe9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a2646970667358221220b235d59478cc58d88c5fefc1274552266b2b2c370d55423d0b9f2d0328dd933f64736f6c634300081c0033f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f3560e01c806303db62b014613a3057806305d1e1dd146135595780630d336011146111f8578063128dd84c1461348157806313bebb82146133a95780631cfc4fb81461326f5780631dbf12bf1461306b57806324900d7c14612f5d57806326e3cc2514612f1a578063374403d714612aa1578063421f9a6e14612a425780634542c92d146128a95780634934f507146124655780634b586fa01461230f5780634c47322e146122cc5780634d0175981461225d5780634da27ac6146122325780634f062c5a1461214f5780634f1ef28614611f115780634f90b4a214611e905780634fe2e75c14611e1a57806352d1902d14611da157806357e10ace14611d7957806366db0e5114611d325780636902d21514611cb9578063715018a614611c46578063720abf89146119de578063869920191461198a578063883c90781461182d5780638d94b36b146117675780638da5cb5b146117355780638f0e15a7146115b5578063900ba78a146114e05780639c2c3fe014611339578063a2b4044e146111fd578063a77da6ba146111f8578063acaace59146111bd578063ad3cb1cc14611172578063ad3e6e1414610fd0578063b35218c014610f45578063b4e4568c14610f14578063bf01c65014610e8a578063bfecf6ce14610d14578063c208e2de14610ceb578063c9fe814b14610c0a578063cf73b2c814610a02578063cfe564c9146108a9578063d7f71ac3146106af578063d940ef7014610678578063e0cc3eb61461056c578063e46cf7a7146103f1578063ea17532e1461033e578063eb9f15be146102e8578063f2fde38b146102bd5763ff2c581414610276575f80fd5b346102b95760203660031901126102b9576004355f52600a60205260ff60405f205460881c165f52600c602052602061ffff60405f205460081c16604051908152f35b5f80fd5b346102b95760203660031901126102b9576102e66102d9613dd9565b6102e1614d7f565b61450e565b005b346102b95760203660031901126102b9576004356001600160401b0381116102b95760ff602061031d81933690600401613dfe565b91908260405193849283378101600d81520301902054166040519015158152f35b346102b95760203660031901126102b9575f6040805161035d81613f13565b82815282602082015201526004355f52600b60205260405f206040519061038382613f13565b549064ffffffffff8216815260ff8260281c1691602082019060078410156103dd576103d48260609564ffffffffff945283604086019360301c168352836040519551168552516020850190613ff2565b51166040820152f35b634e487b7160e01b5f52602160045260245ffd5b346102b95760203660031901126102b9576004356001600160401b0381116102b957610421903690600401613e91565b9061042a614d7f565b5f91601e1982360301915b818410156102e6578360051b810135838112156102b95781018035906001600160401b0382116102b9576020019080360382136102b9576104809161047b913691613f6a565b6151ba565b6040516104ac6020828180820195805191829101875e81015f838201520301601f198101835282613f2e565b5190209160105460ff81161561055d5760081c6001600160401b0316935f5b60ff6010541660ff8216101561054d57600160ff91610546610529898960405161052181610513886020830195869091602192825260ff60f81b9060f81b1660208201520190565b03601f198101835282613f2e565b51902061558a565b8060081c5f526011602052600160ff60405f2092161b8154179055565b01166104cb565b5060019095019493509150610435565b63cdd17b6160e01b5f5260045ffd5b346102b95760203660031901126102b9576004355f52600a60205261ffff60405f2080549061061b6105ac60036105a5600185016143c6565b9301614484565b9160405194846001600160401b038796168652818160401c166020870152818160501c16604087015264ffffffffff8160601c16606087015260ff8160881c16608087015260ff8160901c16151560a087015260981c1660c085015261012060e0850152610120840190613fff565b828103610100840152602080835192838152019201905f5b818110610641575050500390f35b825180516001600160a01b031685526020908101516001600160501b03168186015286955060409094019390920191600101610633565b346102b95761068636613e7b565b905f52600a602052600260405f2001905f52602052602060ff60405f2054166040519015158152f35b346102b9576106bd36614023565b60015460405163e743862960e01b81523360048201526024810184905292939290602090829060449082906001600160a01b03165afa90811561089e575f9161086f575b501561086057815f52600a60205260405f205464ffffffffff8160601c16156108515760901c60ff1661084257610738908261563b565b5f828152600b60205260409020805490929064ffffffffff166108335764ffffffffff835460301c16806107bf575b83546affffffffff0000000000001916603084901b69ffffffff0000000000001617845560408051848152602081018490527f5b4036fee23699edc6c33868b48eea7f82a415ae2715865db9ec6b74cc4701459190a1005b9190818314610824577f5b4036fee23699edc6c33868b48eea7f82a415ae2715865db9ec6b74cc470145937f86ef48d3478dfc87f7a61b90ffdd3193d9862a68632a4c233e71d330b3bb5ce5604080958151908152846020820152a193509091610767565b63177366d160e01b5f5260045ffd5b6376cfa8c160e01b5f5260045ffd5b63142197d960e21b5f5260045ffd5b63d699f7e960e01b5f5260045ffd5b630a71f87f60e31b5f5260045ffd5b610891915060203d602011610897575b6108898183613f2e565b81019061403d565b84610701565b503d61087f565b6040513d5f823e3d90fd5b346102b95760603660031901126102b9576004356024356001600160401b0381116102b9576108dc903690600401613dfe565b60015460405163e743862960e01b815233600482015260448035602483018190529495939492602091839182906001600160a01b03165afa90811561089e575f916109e3575b501561086057805f52600b6020528164ffffffffff60405f205416036109d457805f52600b60205260ff60405f205460281c1660078110156103dd576005116109c55760c883116109b6576109ab7f0ff410db68b95933b3297828599ba335d9c49f904355784c6a2bbbe839541dbc946040519485948552606060208601526060850191614464565b9060408301520390a1005b6347e8b47560e11b5f5260045ffd5b63d52ab5d160e01b5f5260045ffd5b6325d7e13960e11b5f5260045ffd5b6109fc915060203d602011610897576108898183613f2e565b85610922565b346102b957610a1036613ec1565b60015460405163e743862960e01b8152336004820152602481018390529194929190602090829060449082906001600160a01b03165afa90811561089e575f91610beb575b501561086057835f52600b6020528164ffffffffff60405f205416036109d457835f52600b60205260ff60405f205460281c1660078110156103dd576002116109c557815f52600a60205260405f205460ff8160881c165f52600c60205261ffff610ac9838260405f209460501c16614192565b915460081c1610610bdc5763ffffffff82165f5b828110610b1d5750507fac65b29e315dbc954a8da88cc0a5b5c6a59d3223452c29b42b5db6494a4f7d2693610b1891604051948594856141e7565b0390a1005b610b2881848761419f565b35845f52600a602052610b7a60405f20825f526002810160205260405f2060ff198154169055610b5f61ffff825460501c166154d4565b61ffff60501b82549160501b169061ffff60501b1916179055565b5f52600b60205260405f20908464ffffffffff835460301c1603610bcd5781546affffffffffff00000000001984166affffffffffffffffffffff19909116176501000000000017909155600101610add565b63637df6ff60e11b5f5260045ffd5b632b229f6b60e11b5f5260045ffd5b610c04915060203d602011610897576108898183613f2e565b85610a55565b346102b957610c1836613e7b565b600354604051627eeac760e11b81523360048201526024810183905290602090829060449082906001600160a01b03165afa90811561089e575f91610cb9575b5015610caa57805f52600b6020528164ffffffffff60405f205416036109d457815f52600a6020526001600160401b0360405f205416610c9b576102e69161511b565b6315d7cf2960e31b5f5260045ffd5b63078eec1360e11b5f5260045ffd5b90506020813d602011610ce3575b81610cd460209383613f2e565b810103126102b9575183610c58565b3d9150610cc7565b346102b9575f3660031901126102b95760206001600160501b0360045460a01c16604051908152f35b346102b95760403660031901126102b9576004356001600160401b0381116102b957610d44903690600401613e91565b600354604051627eeac760e11b815233600482015260248035908201819052939291602090829060449082906001600160a01b03165afa90811561089e575f91610e58575b5015610caa578015610e4a575f5b818110610de357507f284330b7aeacaa076942744a635969b490cf5cafa294d5b6c311f387078afa2892610dd86040519384936040855260408501916141c3565b9060208301520390a1005b610dee81838561419f565b3590815f52600a602052600260405f2001855f5260205260ff60405f20541615610e3b576001915f52600a602052600260405f2001855f5260205260405f2060ff19815416905501610d97565b6362bfd17560e11b5f5260045ffd5b6254c4fb60e71b5f5260045ffd5b90506020813d602011610e82575b81610e7360209383613f2e565b810103126102b9575184610d89565b3d9150610e66565b346102b95760403660031901126102b9576004356001600160401b0381116102b957610eba903690600401613e91565b610ec2613e6c565b610eca614d7f565b151560ff165f5b828110610eda57005b806001600160a01b03610ef8610ef3600194878961419f565b614231565b165f52600f60205260405f208360ff1982541617905501610ed1565b346102b95760203660031901126102b9576004355f52600a602052602061ffff60405f205460981c16604051908152f35b346102b95760203660031901126102b9576004356001600160501b038116908181036102b9577f7934a8db3f8f75ea0c15b07050e7abbb51f58cfa3914f76450dc170f3d40d81191602091610f98614d7f565b6004805469ffffffffffffffffffff60a01b191660a09290921b69ffffffffffffffffffff60a01b16919091179055604051908152a1005b346102b95760603660031901126102b957602435600435610fef613e2b565b600354604051627eeac760e11b81523360048201526024810185905291929190602090829060449082906001600160a01b03165afa90811561089e575f91611140575b5015610caa57805f52600a60205260ff60405f205460881c165f52600c60205260405f205460ff81169081156108515760ff841691821115611131576110778261458c565b815f52600c6020526001600160501b038060405f205460681c169160681c169003936001600160501b03851161111d5761110a6060946110e06001600160501b037ff926d4ef55149e1fe1f7e953b8bddc590c485497a4f01d73ea589b8150bfed839816614be0565b5f858152600a60205260409020805460ff60881b191660889290921b60ff60881b16919091179055565b60405192835260208301526040820152a1005b634e487b7160e01b5f52601160045260245ffd5b63c59efceb60e01b5f5260045ffd5b90506020813d60201161116a575b8161115b60209383613f2e565b810103126102b9575184611032565b3d915061114e565b346102b9575f3660031901126102b9576111b9604051611193604082613f2e565b60058152640352e302e360dc1b6020820152604051918291602083526020830190613fff565b0390f35b346102b95760203660031901126102b95760206111ee6004355f52600b60205264ffffffffff60405f205416151590565b6040519015158152f35b613e3b565b346102b95760603660031901126102b957600435611219613e6c565b600354604051627eeac760e11b815233600482015260448035602483018190529492602091839182906001600160a01b03165afa90811561089e575f91611307575b5015610caa57825f52600b6020528064ffffffffff60405f205416036109d457825f52600b60205260ff60405f205460281c1660078110156103dd576002116109c5575f818152600a60205260409020805460ff60901b1916831560901b60ff60901b161790557fdef22600eb997387bdb5a7edea2add06b5e92c0fda852986932731166349355192610b18906040519384938460409194939260608201958252151560208201520152565b90506020813d602011611331575b8161132260209383613f2e565b810103126102b957518461125b565b3d9150611315565b346102b95760803660031901126102b95760443560043560243560078310156102b957600354604051627eeac760e11b8152336004820152606435602482018190529391602090829060449082906001600160a01b03165afa90811561089e575f916114ae575b5015610caa57815f52600b6020528064ffffffffff60405f205416036109d457815f52600b60205260ff60405f205460281c1693835f52600b60205260ff60405f205460281c166007811015806103dd57828211156114a0578486036114815760078710156103dd576103dd578511611472575b60078510156103dd578085146114635780851115611457578061143c57506102e69350614db2565b919060066102e695036150a257611452816154e7565b6150a2565b91906102e694506150a2565b635f5db10960e11b5f5260045ffd5b63bea1b71360e01b5f5260045ffd5b5060078610156103dd57851061141457631b1613f760e01b5f5260045ffd5b624da49d60e71b5f5260045ffd5b90506020813d6020116114d8575b816114c960209383613f2e565b810103126102b95751856113a0565b3d91506114bc565b346102b9576114ee36613e7b565b600354604051627eeac760e11b81523360048201526024810183905290602090829060449082906001600160a01b03165afa90811561089e575f91611583575b5015610caa577f86ef48d3478dfc87f7a61b90ffdd3193d9862a68632a4c233e71d330b3bb5ce591816040925f52600b602052825f206affffffffff00000000000019815416905582519182526020820152a1005b90506020813d6020116115ad575b8161159e60209383613f2e565b810103126102b957518361152e565b3d9150611591565b346102b9576115c336614023565b60015460405163e743862960e01b81523360048201526024810184905291939190602090829060449082906001600160a01b03165afa90811561089e575f91611716575b501561086057805f52600a60205260405f20916002830193815f528460205260ff60405f20541615610e3b5761164f825f52600b60205264ffffffffff60405f205416151590565b6108335761165d908361563b565b825460ff8160881c165f52600c60205261ffff8060405f205460081c169160501c161015610bdc576116cf6040937f4029968a3f2d34f7c8df00f90946159de86b935a5c57f786888ce5d1d7845d9c95835f52602052845f2060ff198154169055610b5f61ffff825460501c166154d4565b5f818152600b60209081529084902080546affffffffff0000000000001965ffffffffffff1990911663ffffffff86161765010000000000171690558351928352820152a1005b61172f915060203d602011610897576108898183613f2e565b84611607565b346102b9575f3660031901126102b95760206001600160a01b035f5160206158465f395f51905f525416604051908152f35b346102b95761177536613fa0565b9061177e614d7f565b5f5b8281106117b8576040517feda6ff97e5243629083aff63c9a0f9a5ad50c6d235c74b51e9ec92dc6ad5f6db9080610b188686836142db565b60ff6117cd6117c88386866142bd565b6142cd565b16151580611806575b156117f757806117f16117ec60019386866142bd565b615288565b01611780565b632520e1bd60e11b5f5260045ffd5b5060ff6118176117c88386866142bd565b165f52600c60205260ff60405f205416156117d6565b346102b95761183b36613ec1565b600354604051627eeac760e11b81523360048201526024810183905291929190602090829060449082906001600160a01b03165afa90811561089e575f91611958575b5015610caa57815f52600b6020528364ffffffffff60405f205416036109d457815f52600b60205260ff60405f205460281c1660078110156103dd576002116109c5578015611949575f5b81811061190457507f5ceebf1e578529395e97c10d2075cccca96caf69041edf3d24f9a95bf42b69689391610b1891604051948594856141e7565b61190f81838661419f565b355f52600b60205260405f20908564ffffffffff835460301c1603610bcd5781546affffffffff00000000000019169091556001016118c9565b63d411ab5760e01b5f5260045ffd5b90506020813d602011611982575b8161197360209383613f2e565b810103126102b957518561187e565b3d9150611966565b346102b95760203660031901126102b9576004355f52600b60205264ffffffffff60405f2054165f52600a6020526111b96119ca600160405f20016143c6565b604051918291602083526020830190613fff565b346102b95760803660031901126102b9576004356024356001600160401b0381116102b957611a11903690600401613e91565b916044356001600160401b0381116102b957611a31903690600401613e91565b600354604051627eeac760e11b8152336004820152606435602482018190529396929591602090829060449082906001600160a01b03165afa90811561089e575f91611c14575b5015610caa575f5b828110611a8957005b611a9481848461419f565b3590611aa181888a61419f565b359160078310156102b957600354604051627eeac760e11b81523360048201526024810188905290602090829060449082906001600160a01b03165afa90811561089e575f91611be3575b5015610caa57805f52600b6020528664ffffffffff60405f205416036109d457805f52600b60205260ff60405f205460281c16865f52600b60205260ff60405f205460281c166007811015806103dd57858211156114a057838903611bc45760078310156103dd576103dd578111611472575b60078110156103dd5783811461146357600193879181811115611bb95781611b93575050611b8d9188614db2565b01611a80565b926006611ba69414611bab575b896150a2565b611b8d565b611bb48a6154e7565b611ba0565b50611ba692896150a2565b5060078210156103dd578110611b5f57631b1613f760e01b5f5260045ffd5b90506020813d8211611c0c575b81611bfd60209383613f2e565b810103126102b957518a611aec565b3d9150611bf0565b90506020813d602011611c3e575b81611c2f60209383613f2e565b810103126102b9575187611a78565b3d9150611c22565b346102b9575f3660031901126102b957611c5e614d7f565b5f6001600160a01b035f5160206158465f395f51905f52546001600160601b0360a01b81165f5160206158465f395f51905f5255167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346102b95760203660031901126102b9576004356001600160401b0381116102b957611ce9903690600401613e91565b90611cf2614d7f565b5f5b828110611cfd57005b80611d2c611d0e600193868661419f565b358060081c5f526011602052600160ff60405f2092161b8154179055565b01611cf4565b346102b95760203660031901126102b9576004356001600160401b0381116102b9576111ee611d7461047b611d6d6020943690600401613dfe565b3691613f6a565b615497565b346102b9576020611d92611d8c36613e7b565b90614393565b611d9f6040518092613ff2565bf35b346102b9575f3660031901126102b9576001600160a01b037f000000000000000000000000d5466c2225fd14892ae20881c337ef0f020a3c19163003611e0b5760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b63703e46dd60e11b5f5260045ffd5b346102b95760203660031901126102b95760043561ffff8116908181036102b9577fbb4df7a4ea901e618bc10245eee20e4c8522555bfe10816b8128332bc0e334a991602091611e68614d7f565b6003805461ffff60c81b191660c89290921b61ffff60c81b16919091179055604051908152a1005b346102b957611e9e36613fa0565b611ea6614d7f565b5f5b818110611ee157507fab0ba102ddeb1d896e289b4c68719d8a1f4a198be3ab6883484a1fe929718a4f91610b18604051928392836142db565b80611efd60ff611ef76117c860019587896142bd565b1661458c565b611f0b6117ec8285876142bd565b01611ea8565b60403660031901126102b957611f25613dd9565b6024356001600160401b0381116102b957366023820112156102b957611f55903690602481600401359101613f6a565b6001600160a01b037f000000000000000000000000d5466c2225fd14892ae20881c337ef0f020a3c191680301490811561211a575b50611e0b57611f97614d7f565b6040516352d1902d60e01b81526001600160a01b0383169290602081600481875afa5f91816120e6575b50611fd95783634c9c8ce360e01b5f5260045260245ffd5b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8592036120d45750813b156120c2577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b031916821790557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a28151156120aa575f808360206102e695519101845af43d156120a2573d9161208683613f4f565b926120946040519485613f2e565b83523d5f602085013e6157eb565b6060916157eb565b5050346120b357005b63b398979f60e01b5f5260045ffd5b634c9c8ce360e01b5f5260045260245ffd5b632a87526960e21b5f5260045260245ffd5b9091506020813d602011612112575b8161210260209383613f2e565b810103126102b957519085611fc1565b3d91506120f5565b90506001600160a01b037f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416141583611f8a565b346102b95760203660031901126102b9575f60a060405161216f81613ef8565b82815282602082015282604082015282606082015282608082015201526004355f52600c60205260c060405f206001600160501b03604051916121b183613ef8565b5464ffffffffff60ff82169384815262ffffff6020820161ffff8560081c16815261ffff60408401818760181c168152816060860193858960281c1685528960a060808901988a8c60401c168a52019960681c1689526040519a8b52511660208a0152511660408801525116606086015251166080840152511660a0820152f35b346102b957602061224236613e7b565b5f52600b825264ffffffffff60405f20541614604051908152f35b346102b95760403660031901126102b95760243561ffff811681036102b9576001600160a01b036008541633036122bd576004355f908152600a60205260409020805461ffff60981b191660989290921b61ffff60981b16919091179055005b63993eecd960e01b5f5260045ffd5b346102b95760203660031901126102b9576004355f52600b60205264ffffffffff60405f2054165f52600a602052602060ff60405f205460881c16604051908152f35b346102b95761231d36613ec1565b600354604051627eeac760e11b81523360048201526024810183905292949290602090829060449082906001600160a01b03165afa90811561089e575f91612433575b5015610caa57805f52600b6020528164ffffffffff60405f205416036109d457805f52600b60205260ff60405f205460281c1660078110156103dd576002116109c557815f52600a60205260405f20938015610e4a5760025f9501945b8181106123f7576040517f473f1c3e2a696b3007a52e476d439236942f08e0cff330fc24aeefab41d0d98e9080610b1886868a8a856141e7565b61240281838761419f565b3590815f528660205260ff60405f20541615610e3b576001915f528660205260405f2060ff198154169055016123bd565b90506020813d60201161245d575b8161244e60209383613f2e565b810103126102b9575185612360565b3d9150612441565b346102b95760603660031901126102b9576024356004356001600160401b0382116102b957366023830112156102b9578160040135906001600160401b0382116102b9576024830192602436918460061b0101116102b95760015460405163e743862960e01b815233600482015260448035602483018190529392602091839182906001600160a01b03165afa90811561089e575f9161288a575b501561086057815f52600b6020528064ffffffffff60405f205416036109d457815f52600b60205260ff60405f205460281c1660078110156103dd576005116109c5576005831161287b576125548361420a565b926125626040519485613f2e565b80845261256e8161420a565b602085019590601f19013687376001600160a01b03600654165f5b8381106126ee575050825f52600a602052600360405f2001906801000000000000000083116126da5781548383558084106126b4575b50905f5260205f205f915b8383106126485750505050604051926060840191845260606020850152518091526080830193905f5b818110612629577f72efcd8f70847517ad3bc1486f78c9e8353718305008d04c90e86e8eacea2bdc8580888760408301520390a1005b82516001600160a01b03168652602095860195909201916001016125f3565b60016040826001600160a01b0361265f8495614231565b86546001600160a01b031916911617855561267c60208201614245565b855469ffffffffffffffffffff60a01b191660a09190911b69ffffffffffffffffffff60a01b161785559290940193920191016125ca565b825f528360205f2091820191015b8181106126cf57506125bf565b5f81556001016126c2565b634e487b7160e01b5f52604160045260245ffd5b6126fc610ef3828686614221565b906001600160a01b0360405192633af32abf60e01b84521691826004820152602081602481875afa90811561089e575f9161285d575b501561284e576001600160501b036127566020612750848989614221565b01614245565b166102d181036127da57506040516301ffc9a760e01b81526380ac58cd60e01b6004820152602081602481865afa90811561089e575f916127bc575b50156127ad576001915b6127a6828a614296565b5201612589565b63adc1f5ed60e01b5f5260045ffd5b6127d4915060203d8111610897576108898183613f2e565b8a612792565b6104830361283f576040516301ffc9a760e01b8152636cdb3d1360e11b6004820152602081602481865afa90811561089e575f91612821575b50156127ad5760019161279c565b612839915060203d8111610897576108898183613f2e565b8a612813565b630c1516e360e31b5f5260045ffd5b63f8f94f4960e01b5f5260045ffd5b612875915060203d8111610897576108898183613f2e565b8a612732565b636e58b71560e01b5f5260045ffd5b6128a3915060203d602011610897576108898183613f2e565b85612500565b346102b9576128b736613ec1565b600354604051627eeac760e11b81523360048201526024810183905291929190602090829060449082906001600160a01b03165afa90811561089e575f91612a10575b5015610caa57815f52600b6020528364ffffffffff60405f205416036109d457815f52600b60205260ff60405f205460281c1660078110156103dd576002116109c557835f52600a60205260405f205460ff8160881c165f52600c60205261ffff61296e838260405f209460501c16614192565b915460081c1610610bdc575f5b8181106129b657507f5b1a562619687202d271aec5b478cdde4e6e135bbdec7a5f7a11bf3056b534269391610b1891604051948594856141e7565b6129c181838661419f565b3590855f52600a602052600260405f2001825f528060205260ff60405f205416612a01576001925f5260205260405f208260ff198254161790550161297b565b63cbc57df560e01b5f5260045ffd5b90506020813d602011612a3a575b81612a2b60209383613f2e565b810103126102b95751856128fa565b3d9150612a1e565b346102b957612a5036613e7b565b90815f52600b60205264ffffffffff60405f2054161480612a79575b6020906040519015158152f35b505f52600b60205260ff60405f205460281c1660078110156103dd5760046020911015612a6c565b346102b9576101803660031901126102b957612abb613dd9565b6064356001600160401b0381116102b957612ada903690600401613dfe565b91906084356001600160401b0381116102b957612afb903690600401613dfe565b60a4949194356001600160401b0381116102b957612b1d903690600401613dfe565b60c4969196356001600160401b0381116102b957612b3f903690600401613dfe565b939092610164359687151588036102b9576001600160a01b03601254163303612f0b576044355f908152600a60205260409020805471ff000000000000000000ffffffffffffffff19166024356001600160401b0316176101243560881b60ff60881b1617815596875461ffff60401b191660e43560401b61ffff60401b16178855875466ffffffffffffff60501b19166101043560601b64ffffffffff60601b1617600160501b178855875461ffff60981b19166101443560981b61ffff60981b161788556024355f908152600b60205260409020805465ffffffffffff191660443563ffffffff1617650600000000001790556001600160401b0381116126da57612c5c81612c5360018b01546140e0565b60018b0161412e565b5f601f8211600114612e645795899791955f9c7fbd0ab160e05d3247ed89c8619b640c22634a65d36949c146e3133fc319ccfb4d96612d1e968f60209f9d9b97612db89f9b8d6001612cca8380612cfb9e612cf69891612e59575b508160011b915f199060031b1c19161790565b9101555b612ce4612cdf61047b368486613f6a565b614173565b805460ff191660011790553691613f6a565b614b40565b60405191829160443583526024358b84015260c0604084015260c0830190614055565b60e43560608301526101243560808301526101043560a08301520390a1612df0575b50507fd0f236da2a55211dd4cf5df9ab238a0858e8d127f01ce8d39d4299657e6d10c86040805160443581526101443586820152a16001600160a01b036002541690604051948580948193638471af7760e01b83526044359060048401602090939291936001600160a01b0360408201951681520152565b03925af1801561089e57612dc857005b6102e69060203d602011612de9575b612de18183613f2e565b8101906140ad565b503d612dd7565b805460ff60901b191682151560901b60ff60901b161790557fdef22600eb997387bdb5a7edea2add06b5e92c0fda852986932731166349355190612e4f9060408051604435815291156020830152602435908201529081906060820190565b0390a18480612d40565b90508601355f612cb7565b600189015f5260205f20905f5b601f1984168110612ef35750955f9c7fbd0ab160e05d3247ed89c8619b640c22634a65d36949c146e3133fc319ccfb4d96612d1e96612cf660209f9d9b9597612db89f9d97612cfb998f9d82601f19811610612eda575b50508d60018083811b01910155612cce565b8301355f19600384901b60f8161c191690555f80612ec8565b90916020600181928588013581550193019101612e71565b637fea9dc560e01b5f5260045ffd5b346102b95760203660031901126102b9576004355f52600a60205260ff60405f205460881c165f52600c602052602061ffff60405f205460181c16604051908152f35b346102b95760a03660031901126102b9576004356001600160a01b0381168091036102b9576024356001600160a01b0381168091036102b957612f9e613dad565b91612fa7613dc3565b90608435936001600160a01b0385168095036102b957612fc5614d7f565b600254936001600160a01b038516818115918215613061575b505015613052576001600160a01b039485936001600160601b0360a01b60015416176001556001600160601b0360a01b1617600255166001600160601b0360a01b6007541617600755166001600160601b0360a01b60085416176008556001600160601b0360a01b60095416176009555f80f35b6337b8bea960e21b5f5260045ffd5b1490508188612fde565b346102b95760e03660031901126102b9576004356024356001600160401b0381116102b95761309e903690600401613dfe565b90916044356001600160401b0381116102b9576130bf903690600401613dfe565b9290936064356001600160401b0381116102b9576130e1903690600401613dfe565b6084939193356001600160401b0381116102b957613103903690600401613dfe565b60015460405163e743862960e01b815233600482015260c435602482018190529960a4359893959392909190602090829060449082906001600160a01b03165afa90811561089e575f91613250575b501561086057895f52600b6020528864ffffffffff60405f205416036109d457895f52600b60205260ff60405f205460281c1660078110156103dd576005116109c5577fff9c4aa2c485437fd53cb8c1462dd050bd76f4e240718699dd1bb1bb248c6be49a6131f861320b97613229998c5f52600a60205260ff60405f205460881c165f52600c6020526131f262ffffff60405f205460281c168d6145b1565b8c6145df565b613234575b612cf6878787878787614968565b60405194859485526020850152608060408501526080840190614055565b9060608301520390a1005b61324b6001600160501b0360045460a01c16614be0565b6131fd565b613269915060203d602011610897576108898183613f2e565b8c613152565b346102b95760603660031901126102b95760443560243560043560078310156102b9575f818152600a6020908152604091829020546003549251627eeac760e11b81523360048201526001600160401b03909116602482015291829060449082906001600160a01b03165afa90811561089e575f91613377575b5015610caa57815f52600b6020528064ffffffffff60405f205416036109d457805f52600a6020526001600160401b0360405f2054169283831461336857801561335957600681101561334a5783613345916102e695846150a2565b61511b565b63cbcd7cd360e01b5f5260045ffd5b50613345836102e69483614db2565b633493d67f60e11b5f5260045ffd5b90506020813d6020116133a1575b8161339260209383613f2e565b810103126102b95751846132e9565b3d9150613385565b346102b95760603660031901126102b9576004356024359064ffffffffff82168092036102b957604435908115158092036102b957335f52600f60205260ff60405f2054161561347257805f52600a60205260405f20918254938064ffffffffff8660a81c16019364ffffffffff851161111d577ff34537c7169eedc3a5bf2996afbe8ee8754382962b6a4276ee7d450e49748a0f9560609564ffffffffff60a81b9060a81b169064ffffffffff60a81b191617905560405192835260208301526040820152a1005b63e5e9973760e01b5f5260045ffd5b346102b95760603660031901126102b95760043560ff8116908181036102b9576024359160ff8316928381036102b9576134b9613e2b565b6134c1614d7f565b606460ff6134d8836134d386896140cc565b6140cc565b160361354a577fb4ae64ee51c0d84410a16847b3f8f3d7ac7bbbeaf2abae8671beba816f0279e39460609460ff936005548560b01b8560b01b16928660a01b9060a01b169062ffffff60a01b191617908560a81b9060a81b1617176005556040519384526020840152166040820152a1005b63196f075b60e11b5f5260045ffd5b346102b95760e03660031901126102b9576024356001600160401b0381116102b957613589903690600401613dfe565b906044356001600160401b0381116102b9576135a9903690600401613dfe565b9190926064356001600160401b0381116102b9576135cb903690600401613dfe565b94906084356001600160401b0381116102b9576135ec903690600401613dfe565b96909160a4359661ffff8816948589036102b95760c4359660ff881688036102b95760015460405163e743862960e01b81523360048083019190915235602482015290602090829060449082906001600160a01b03165afa90811561089e575f91613a11575b5015610860576136766004355f52600b60205264ffffffffff60405f205416151590565b6108335760ff88165f52600c60205260405f209861369660ff8a1661458c565b6136a962ffffff8b5460281c16896145b1565b6003549a64ffffffffff808d60a01c161461111d5764ffffffffff60a01b198c1660a08d811c64ffffffffff166001810190911b64ffffffffff60a01b16919091176003555f908152600a60205260409020805471ff000000000000000000ffffffffffffffff19166004356001600160401b03161760888c901b60ff60881b161781556137969161375190825461ffff60401b191660409190911b61ffff60401b16178255565b805466ffffffffffffff60501b19164260601b64ffffffffff60601b1617600160501b178155600354815461ffff60981b191660309190911c61ffff60981b16179055565b6004355f908152600b60205260409020805465ffffffffffff191660a08d901c63ffffffff161765060000000000179081905560301c64ffffffffff16806138c9575b50613845612db89c6001600160501b039864ffffffffff9c987fbd0ab160e05d3247ed89c8619b640c22634a65d36949c146e3133fc319ccfb4d9860209f9d97988f998f61388b9f9960ff9961386c9d6138369360a01c166145df565b50612cf6878787878787614968565b926040519485948c8c60a01c1686526004358e87015260c0604087015260c0860190614055565b9260608501521660808301524260a08301520390a15460681c16614be0565b600254604051638471af7760e01b815233600482015260a09290921c90921660248201529283916001600160a01b03169082905f9082906044820190565b600354604051627eeac760e11b815233600480830191909152356024820152989c979b979a9699959894969395939490602090829060449082906001600160a01b03165afa90811561089e575f916139df575b5015610caa57612db89c60209c64ffffffffff9c61388b9b6001600160501b039b8f9a8f61386c9b60ff997fbd0ab160e05d3247ed89c8619b640c22634a65d36949c146e3133fc319ccfb4d9e613836937f86ef48d3478dfc87f7a61b90ffdd3193d9862a68632a4c233e71d330b3bb5ce560406138459d6004355f526020600b9052815f206affffffffff00000000000019815416905581519081526004356020820152a150509950999f5050989e9f5098509850989c9d5098509c506137d9565b90506020813d602011613a09575b816139fa60209383613f2e565b810103126102b957518e61391c565b3d91506139ed565b613a2a915060203d602011610897576108898183613f2e565b8c613652565b346102b9576101203660031901126102b9576004356001600160a01b0381168091036102b957602435906001600160a01b0382168092036102b957613a73613dad565b90613a7c613dc3565b92608435906001600160501b038216938483036102b95760a4356001600160a01b0381168091036102b95760c4359261ffff8416938481036102b95760e4359664ffffffffff881688036102b95761010435986001600160a01b038a16809a036102b9575f5160206158665f395f51905f52549060ff8260401c16159b8c6001600160401b03841680159182613da5575b506001149081613d9b575b159081613d92575b50613d83577fbb4df7a4ea901e618bc10245eee20e4c8522555bfe10816b8128332bc0e334a99a60209a7f7934a8db3f8f75ea0c15b07050e7abbb51f58cfa3914f76450dc170f3d40d811958f958d9660016001600160401b03198316175f5160206158665f395f51905f5255613d57575b50613b9b61555f565b613ba361555f565b613bac3361450e565b613bb461555f565b6001600160601b0360a01b5f5416175f556001600160a01b036003549a16916001600160a01b0360045494846001600160601b0360a01b871617600455166001600160601b0360a01b600554161760055564ffffffffff60a01b9060a01b1698898966ffffffffffffff60c81b8d1617176003556001600160601b0360a01b6006541617600655613c43614d7f565b7fffff0000000000000000000000000000000000000000000000000000000000009092161760a09190911b69ffffffffffffffffffff60a01b1617600455604051908152a1613c90614d7f565b7fffffffffff000000000000000000000000000000000000000000000000000000909316171760c89190911b61ffff60c81b1617600355604051908152a16010805468ffffffffffffffffff1916630668a004179055601280546001600160a01b031916919091179055613d0057005b68ff0000000000000000195f5160206158665f395f51905f5254165f5160206158665f395f51905f52557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a1005b68ffffffffffffffffff191668010000000000000001175f5160206158665f395f51905f52555f613b92565b63f92ee8a960e01b5f5260045ffd5b9050158e613b20565b303b159150613b18565b91508e613b0d565b604435906001600160a01b03821682036102b957565b606435906001600160a01b03821682036102b957565b600435906001600160a01b03821682036102b957565b359061ffff821682036102b957565b9181601f840112156102b9578235916001600160401b0383116102b957602083818601950101116102b957565b6044359060ff821682036102b957565b346102b95760203660031901126102b9576004355f52600b602052602064ffffffffff60405f205416604051908152f35b6024359081151582036102b957565b60409060031901126102b9576004359060243590565b9181601f840112156102b9578235916001600160401b0383116102b9576020808501948460051b0101116102b957565b9060606003198301126102b95760043591602435906001600160401b0382116102b957613ef091600401613e91565b909160443590565b60c081019081106001600160401b038211176126da57604052565b606081019081106001600160401b038211176126da57604052565b90601f801991011681019081106001600160401b038211176126da57604052565b6001600160401b0381116126da57601f01601f191660200190565b929192613f7682613f4f565b91613f846040519384613f2e565b8294818452818301116102b9578281602093845f960137010152565b9060206003198301126102b9576004356001600160401b0381116102b957826023820112156102b9578060040135926001600160401b0384116102b957602460c08502830101116102b9576024019190565b9060078210156103dd5752565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b60609060031901126102b957600435906024359060443590565b908160209103126102b9575180151581036102b95790565b9080602083519182815201916020808360051b8301019401925f915b83831061408057505050505090565b909192939460208061409e600193601f198682030187528951613fff565b97019301930191939290614071565b908160209103126102b957516001600160a01b03811681036102b95790565b9060ff8091169116019060ff821161111d57565b90600182811c9216801561410e575b60208310146140fa57565b634e487b7160e01b5f52602260045260245ffd5b91607f16916140ef565b818110614123575050565b5f8155600101614118565b9190601f811161413d57505050565b614167925f5260205f20906020601f840160051c83019310614169575b601f0160051c0190614118565b565b909150819061415a565b60208091604051928184925191829101835e8101600d81520301902090565b9190820180921161111d57565b91908110156141af5760051b0190565b634e487b7160e01b5f52603260045260245ffd5b81835290916001600160fb1b0383116102b95760209260051b809284830137010190565b949392916040926142059287526060602088015260608701916141c3565b930152565b6001600160401b0381116126da5760051b60200190565b91908110156141af5760061b0190565b356001600160a01b03811681036102b95790565b356001600160501b03811681036102b95790565b8051156141af5760200190565b8051600110156141af5760400190565b8051600210156141af5760600190565b8051600310156141af5760800190565b80518210156141af5760209160051b010190565b8181029291811591840414171561111d57565b91908110156141af5760c0020190565b3560ff811681036102b95790565b60208082528101839052604001915f905b8082106142f95750505090565b909192833560ff81168091036102b957815261ffff61431a60208601613def565b16602082015261ffff61432f60408601613def565b166040820152606084013562ffffff81168091036102b9576060820152608084013564ffffffffff81168091036102b957608082015260a0840135906001600160501b0382168092036102b95760c08160019360a0839401520194019201906142ec565b815f52600b60205264ffffffffff60405f205416146143b157505f90565b5f52600b60205260ff60405f205460281c1690565b9060405191825f8254926143d9846140e0565b808452936001811690811561444257506001146143fe575b5061416792500383613f2e565b90505f9291925260205f20905f915b818310614426575050906020614167928201015f6143f1565b602091935080600191548385890101520191019091849261440d565b90506020925061416794915060ff191682840152151560051b8201015f6143f1565b908060209392818452848401375f828201840152601f01601f1916010190565b908154916144918361420a565b9261449f6040519485613f2e565b80845260208401915f5260205f20915f905b8282106144be5750505050565b604051604081018181106001600160401b038211176126da5760019283926020926040526001600160501b0388546001600160a01b038116835260a01c16838201528152019401910190926144b1565b6001600160a01b03168015614579576001600160a01b035f5160206158465f395f51905f5254826001600160601b0360a01b8216175f5160206158465f395f51905f5255167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3565b631e4fbdf760e01b5f525f60045260245ffd5b5f52600c60205260ff60405f205416156145a257565b630bec67bf60e31b5f5260045ffd5b9081151591826145d4575b5050156145c557565b6305615c0b60e11b5f5260045ffd5b111590505f806145bc565b9192905f6146209173e3223eaf0e260b54a8ce777ac9f4a972310370c095604051938492839263b2dad15560e01b8452602060048501526024840191614464565b0381875af490811561089e575f916148ef575b50809360038251106148e05760148251116148d157602060405180926391ae2cdb60e01b8252826004830152818061466e6024820188613fff565b03915af490811561089e575f916148b2575b50156148a35761468f816151ba565b925f52600a602052600160405f2001906146ab61047b836143c6565b9360405194602086018151966146db6020828186019a808c875e81015f838201520301601f198101835282613f2e565b51902091604051926020840182519461470e60208281870198808a875e81015f838201520301601f198101835282613f2e565b51902014159687614722575b505050505050565b61472b82615497565b61487d5760ff60405160208185518089835e8101600d815203019020541661486e5760209251908161484a575b5050604051928391518091835e8101600d815203019020600160ff198254161790558051906001600160401b0382116126da5761479f8261479985546140e0565b8561412e565b602090601f83116001146147e7576147ce92915f91836147dc575b50508160011b915f199060031b1c19161790565b90555b5f808080808061471a565b015190505f806147ba565b90601f19831691845f52815f20925f5b818110614832575090846001959493921061481a575b505050811b0190556147d1565b01515f1960f88460031b161c191690555f808061480d565b929360206001819287860151815501950193016147f7565b839082604051938492835e8101600d81520301902060ff1981541690555f80614758565b633480a50360e21b5f5260045ffd5b604051633b4c9c8960e21b8152602060048201528061489f6024820185613fff565b0390fd5b634caa0cad60e11b5f5260045ffd5b6148cb915060203d602011610897576108898183613f2e565b5f614680565b63680b6caf60e01b5f5260045ffd5b6365ac5e6760e11b5f5260045ffd5b90503d805f833e6149008183613f2e565b8101906020818303126102b9578051906001600160401b0382116102b9570181601f820112156102b95780519061493682613f4f565b926149446040519485613f2e565b828452602083830101116102b957815f9260208093018386015e830101525f614633565b949392909460198611614b315785158015614b26575b15614b175760206149c19173e3223eaf0e260b54a8ce777ac9f4a972310370c09760405193849283926309f241ed60e41b84528560048501526024840191614464565b0381895af490811561089e575f91614af8575b5015614ae95760198111614ada57614a0b9160209160405193849283926309f241ed60e41b84528560048501526024840191614464565b0381875af490811561089e575f91614abb575b5015614aad5760198211614a9e57614a56926020926040518095819482936309f241ed60e41b84528760048501526024840191614464565b03915af490811561089e575f91614a7f575b5015614a7057565b6388d04d1f60e01b5f5260045ffd5b614a98915060203d602011610897576108898183613f2e565b5f614a68565b6348997f7160e01b5f5260045ffd5b620f7f1b60e91b5f5260045ffd5b614ad4915060203d602011610897576108898183613f2e565b5f614a1e565b6304121ae360e31b5f5260045ffd5b63088cc68560e21b5f5260045ffd5b614b11915060203d602011610897576108898183613f2e565b5f6149d4565b638c72c2a160e01b5f5260045ffd5b50600486101561497e565b630da1592560e21b5f5260045ffd5b9695929194939060405195614b5660a088613f2e565b6004875260805f5b818110614bcf57505092614bbd9492614b97614baa93899896614bcc9a9c614b858b614259565b52614b8f8a614259565b503691613f6a565b614ba088614266565b52614b8f87614266565b614bb385614276565b52614b8f84614276565b614bc682614286565b52614286565b50565b806060602080938c01015201614b5e565b5f906001600160a01b035f54166064614c0160ff60055460a01c16846142aa565b04813b156102b95760405163079cc67960e41b815233600482015260248101919091525f8160448183865af1801561089e57614d6a575b50614c9360206001600160a01b03600454166064614c5e60ff60055460a81c16876142aa565b6040516323b872dd60e01b81523360048201526001600160a01b03909316602484015204604482015291829081906064820190565b038187865af18015614d5f5791849391602093614d0d9650614d44575b506001600160a01b03846064614cd06005549560ff8760b01c16906142aa565b0493604051978895869485936323b872dd60e01b85521633600485016001600160a01b036040929594938160608401971683521660208201520152565b03925af1908115614d385750614d205750565b614bcc9060203d602011610897576108898183613f2e565b604051903d90823e3d90fd5b614d5a90843d8611610897576108898183613f2e565b614cb0565b6040513d86823e3d90fd5b614d779193505f90613f2e565b5f915f614c38565b6001600160a01b035f5160206158465f395f51905f5254163303614d9f57565b63118cdaa760e01b5f523360045260245ffd5b905f92825f52600a60205260405f20826001600160401b0382541614615094575b61ffff815460501c1690811561111d5780546bffff0000000000000000000019165f1990920160501b61ffff60501b1691909117815561ffff905460501c166150585750815f52600a60205260405f2061ffff815460501c1661504957612cdf61047b6001614e4293016143c6565b60ff198154169055815f52600a602052600360405f205f815560018101614e6981546140e0565b9081615006575b5050018054905f815581614fe8575b50507fe5cdc676962c829e36cbb7b0c60a322b60e2d01db199590aab156803c5a97ae66020604051848152a15b5f818152600b60205260409020805465ffffffffffff191690556007546001600160a01b0316803b156102b9575f8091604460405180948193637792156760e11b83528860048401528760248401525af1801561089e57614fd3575b506001600160a01b0360085416803b15614fb657838091604460405180948193637792156760e11b83528860048401528760248401525af18015614d5f57908491614fba575b50506001600160a01b036009541691823b15614fb6579060448492836040519586948593637792156760e11b8552600485015260248401525af18015614fab57614f96575050565b614fa1828092613f2e565b614fa85750565b80fd5b6040513d84823e3d90fd5b8380fd5b81614fc491613f2e565b614fcf57825f614f4e565b8280fd5b614fe09193505f90613f2e565b5f915f614f08565b5f5260205f20908101905b81811015614e7f575f8155600101614ff3565b81601f5f931160011461501d5750555b5f80614e70565b8183526020832061503991601f0160051c810190600101614118565b8082528160208120915555615016565b6370141ec960e11b5f5260045ffd5b60607fa21e607ac5445b7603f2e7e913bb2a66f680c289fb1a2ce735eb6c92713c5dd991604051908582528460208301526040820152a1614eac565b61509d846154e7565b614dd3565b909192825f52600b60205260405f2060078510156103dd577f57ff30d4395dd2630a292b8a50feba238650ef9136dd5961908b899bb1ba99169460809482615113935465ff00000000008460281b169065ff0000000000191617905560405194855260208501526040840190613ff2565b6060820152a1565b5f818152600a60209081526040808320805467ffffffffffffffff19166001600160401b038716179055600e8252808320805464ffffffffff19169055848352600b825291829020805465ff000000000019166506000000000017905581519283528201929092527fb36e6603e4ebbca03e98d06ae8a134eb0c59baf95557ee9403a9fe073b6ac0749190a1565b9081518110156141af570160200190565b906151e8602080936040519481869251918291018484015e81015f838201520301601f198101845283613f2e565b5f5b8251811015614bcc576151fd81846151a9565b51604160f81b6001600160f81b0319821690811015908161525a575b50615228575b506001016151ea565b60f81c6020019060ff821161111d5760019160f81b6001600160f81b0319165f1a61525382866151a9565b539061521f565b602d60f91b101590505f615219565b3561ffff811681036102b95790565b3562ffffff811681036102b95790565b60ff615293826142cd565b1660018111615376575b5f52600c60205260405f209060ff6152b4826142cd565b1660ff198354161782556152ca60208201615269565b9082549064ffff0000006152e060408301615269565b60181b169167ffffff00000000006152fa60608401615278565b60281b169060808301359364ffffffffff851685036102b9576cffffffffff00000000000000009369ffffffffffffffffffff60681b9061533d9060a001614245565b60681b16956001600160501b0360681b199262ffff0086199260081b169067ffffffffffffff00191617161716179160401b1617179055565b5f19810181811161111d575f52600c60205260405f206040519061539982613ef8565b549060ff821681526020810161ffff8360081c16815260a0604083019261ffff8560181c1684526001600160501b03606082019562ffffff8160281c16875264ffffffffff8160401c16608084015260681c1691829101526001600160501b0361540560a08801614245565b16106154885761ffff8061541b60208801615269565b9251169116106154795761ffff8061543560408701615269565b92511691161061546a5762ffffff8061545060608601615278565b9251169116101561529d57633f07a64560e01b5f5260045ffd5b6383202d3d60e01b5f5260045ffd5b637dc1f31b60e01b5f5260045ffd5b636dddf41160e11b5f5260045ffd5b6154d1906040516154c76020828180820195805191829101875e81015f838201520301601f198101835282613f2e565b51902060106155a8565b90565b61ffff1661ffff811461111d5760010190565b5f818152600a60209081526040808320805467ffffffffffffffff198116909155600e835292819020805464ffffffffff19164264ffffffffff1617905580519384526001600160401b0392909216908301527f81ec2aca54fed63234990c222e20c265fbd80545574659c8e359fe9dc3cda16291a1565b60ff5f5160206158665f395f51905f525460401c161561557b57565b631afcd79f60e31b5f5260045ffd5b8115615594570690565b634e487b7160e01b5f52601260045260245ffd5b805460ff8116939290841561055d5760081c6001600160401b0316905f5b8560ff8216106155da575060019450505050565b600160ff61560e85604051602081019061052181610513898b869091602192825260ff60f81b9060f81b1660208201520190565b8060081c5f52828701602052161b60405f205416156156325760010160ff166155c6565b505f9450505050565b9190915f52600a602052615654600360405f2001614484565b91825161566057509050565b5f90815b84518310156157d0576104836001600160501b0360206156848689614296565b51015116036157245780156156a0575b600190925b0191615664565b506001600160a01b036156b38386614296565b5151604051627eeac760e11b8152336004820152602481018490529160209183916044918391165afa801561089e575f906156f2575b15159050615694565b506020813d821161571c575b8161570b60209383613f2e565b810103126102b957600190516156e9565b3d91506156fe565b916102d16001600160501b03602061573c8489614296565b510151161461574e575b600190615699565b91801561575c575b91615746565b50602460206001600160a01b036157738588614296565b515116604051928380926331a9108f60e11b82528660048301525afa801561089e576001915f916157b2575b506001600160a01b031633149050615756565b6157ca915060203d8111612de957612de18183613f2e565b5f61579f565b9293505050156157dc57565b63466e5a3d60e11b5f5260045ffd5b9061580f575080511561580057805190602001fd5b63d6bda27560e01b5f5260045ffd5b8151158061583c575b615820575090565b6001600160a01b0390639996b31560e01b5f521660045260245ffd5b50803b1561581856fe9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a2646970667358221220b235d59478cc58d88c5fefc1274552266b2b2c370d55423d0b9f2d0328dd933f64736f6c634300081c0033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.