Source Code
Overview
S Balance
S Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
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 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 {IBank} from "../interfaces/IBank.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";
import {IActivityPoints, IActivityPointsCaller, ActivityType} from "../ActivityPoints/interfaces/IActivityPoints.sol";
contract Clans is UUPSUpgradeable, OwnableUpgradeable, IClans, IActivityPointsCaller {
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 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: unused
address private _bridge; // TODO: Bridge Can remove later if no longer need the bridge
IActivityPoints private _activityPoints;
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 treasury,
address dev,
uint80 editNameCost,
address paintswapMarketplaceWhitelist,
uint16 initialMMR,
uint40 startClanId,
address bridge,
IActivityPoints activityPoints
) external initializer {
__Ownable_init(_msgSender());
__UUPSUpgradeable_init();
_brush = brush;
_playerNFT = playerNFT;
_treasury = treasury;
_dev = dev;
_nextClanId = startClanId;
_paintswapMarketplaceWhitelist = paintswapMarketplaceWhitelist;
setEditNameCost(editNameCost);
setInitialMMR(initialMMR);
_bridge = bridge;
_activityPoints = activityPoints;
}
// TODO: remove in prod
function setActivityPoints(address activityPoints) external override onlyOwner {
_activityPoints = IActivityPoints(activityPoints);
}
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);
}
address msgSender = _msgSender();
(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);
_activityPoints.rewardBlueTickets(
ActivityType.clans_evt_clancreated,
_bankFactory.createBank(msgSender, clanId),
_players.isPlayerEvolved(playerId),
1
);
}
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(!_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 && !foundNFT; ++i) {
if (nftInfo[i].nftType == 1155) {
foundNFT = IERC1155(nftInfo[i].nft).balanceOf(sender, gateKeepTokenId) != 0;
} else if (nftInfo[i].nftType == 721) {
try IERC721(nftInfo[i].nft).ownerOf(gateKeepTokenId) returns (address nftOwner) {
foundNFT = nftOwner == sender;
} catch {
// Reverting is fine, the tokenId just might not exist
}
}
}
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
);
}
/// @dev used by Territories to get Clan Bank for Activity Points
function getClanBankAddress(uint256 clanId) external view override returns (address bankAddress) {
return _bankFactory.getBankAddress(clanId);
}
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);
}
// 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;
enum ActivityType {
NONE,
//
// BLUE TICKETS
//
instantactions_evt_doinstantactions,
instantvrfactions_evt_doinstantvrfactions,
passiveactions_evt_claimpassiveaction,
quests_evt_questcompleted,
shop_evt_buy, // + shop_evt_buybatch,
shop_evt_sell, // + shop_evt_sellbatch,
wishingwell_evt_donate,
wishingwell_evt_donatetoclan,
orderbook_evt_ordersmatched,
orderbook_evt_claimedtokens,
orderbook_evt_claimednfts,
// players
players_evt_actionfinished,
players_evt_addxp,
players_evt_levelup,
players_evt_boostfinished,
players_evt_dailyreward,
players_evt_weeklyreward,
players_evt_claimedxpthresholdrewards,
// clans
clans_evt_clancreated, // _isClanActivityType
lockedbankvaults_evt_attackvaults, // _isClanActivityType
territories_evt_attackterritory, // _isClanActivityType
territories_evt_claimunoccupiedterritory, // _isClanActivityType
//
// GREEN TICKETS
//
players_dailyreward, // = 8
wishingwell_luckofthedraw, // = 3
wishingwell_luckypotion // = 50
}
interface IActivityPointsCaller {
function setActivityPoints(address activityPoints) external;
}
interface IActivityPoints {
event ActivityPointsEarned(
ActivityType indexed activityType,
uint256 value,
address indexed from,
uint16 indexed tokenId,
uint256 points
);
function rewardGreenTickets(
ActivityType activityType,
address recipient,
bool isEvolved
) external returns (uint256 tickets);
function rewardBlueTickets(
ActivityType activityType,
address from,
bool isEvolvedOrNA,
uint256 value
) external returns (uint256 points);
}// 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;
// 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;
// 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;
uint16 constant SPECIAL_BASE = 13312;
uint16 constant SPECIAL_MAX = 13567;
// 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 and also things like extra boost info
struct BoostInfo {
uint40 startTime;
uint24 duration;
uint16 value;
uint16 itemTokenId; // Get the effect of it
BoostType boostType;
}
struct StandardBoostInfo {
uint40 startTime;
uint24 duration;
uint16 value;
uint16 itemTokenId; // Get the effect of it
BoostType boostType;
// Another boost slot for storing the last boost someone had active
uint40 lastStartTime;
uint24 lastDuration;
uint16 lastValue;
uint16 lastItemTokenId;
BoostType lastBoostType;
uint40 cooldown; // Just put here for packing
}
struct ExtendedBoostInfo {
uint40 startTime;
uint24 duration;
uint16 value;
uint16 itemTokenId; // Get the effect of it
BoostType boostType;
// Last boost
uint40 lastStartTime;
uint24 lastDuration;
uint16 lastValue;
uint16 lastItemTokenId;
BoostType lastBoostType;
// Others
uint40 cooldown; // Just put here for packing
bytes1 packedData; // 1st bit is hasExtraBoost. This is typically for a wish boost. Gas optimization to read this first
// New storage slot for another boost (only wish boost for heroes currently)
uint40 extraStartTime;
uint24 extraDuration;
uint16 extraValue;
uint16 extraItemTokenId;
BoostType extraBoostType;
// Last extra boost
uint40 lastExtraStartTime;
uint24 lastExtraDuration;
uint16 lastExtraValue;
uint16 lastExtraItemTokenId;
BoostType lastExtraBoostType;
}
// 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;
}
struct PlayerInfo {
uint24 avatarId;
uint24 originalAvatarId; // The base avatar id that you were born with
uint40 mintedTimestamp;
uint40 upgradedTimestamp; // What time you upgraded your avatar
}
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;
// Player Boost
uint256 constant HAS_EXTRA_BOOST_BIT = 1;
uint256 constant BOOST_VERSION_BIT = 0;// 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;
function getClanBankAddress(uint256 clanId) external view returns (address);
}// 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 (ExtendedBoostInfo 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
Contract ABI
API[{"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":[],"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"},{"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":[{"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":"clanId","type":"uint256"}],"name":"getClanBankAddress","outputs":[{"internalType":"address","name":"bankAddress","type":"address"}],"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":"treasury","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"},{"internalType":"contract IActivityPoints","name":"activityPoints","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":"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":"address","name":"activityPoints","type":"address"}],"name":"setActivityPoints","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":"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
60a080604052346100c257306080525f5160206157be5f395f51905f525460ff8160401c166100b3576002600160401b03196001600160401b03821601610060575b6040516156f790816100c78239608051818181611bb90152611d5d0152f35b6001600160401b0319166001600160401b039081175f5160206157be5f395f51905f525581527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f80610041565b63f92ee8a960e01b5f5260045ffd5b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c90816305d1e1dd14613763575080630d336011146110b6578063128dd84c1461368b57806313bebb82146135b35780631cfc4fb8146134795780631dbf12bf1461327557806324900d7c1461316757806326e3cc25146131245780632898d53014612d98578063374403d71461292a5780633bde56441461289f578063421f9a6e146128405780634542c92d146126a75780634934f507146122635780634b586fa01461210d5780634c47322e146120ca5780634d0175981461205b5780634da27ac6146120305780634f062c5a14611f4d5780634f1ef28614611d0f5780634f90b4a214611c8e5780634fe2e75c14611c1857806352d1902d14611b9f57806357e10ace14611b77578063715018a614611b04578063720abf891461189c5780638699201914611848578063883c9078146116eb5780638d94b36b146116255780638da5cb5b146115f35780638f0e15a714611473578063900ba78a1461139e5780639c2c3fe0146111f7578063a2b4044e146110bb578063a77da6ba146110b6578063acaace591461107b578063acfda69e1461103a578063ad3cb1cc14610fef578063ad3e6e1414610e4d578063b35218c014610dc2578063b4e4568c14610d91578063bf01c65014610d07578063bfecf6ce14610b91578063c208e2de14610b68578063c9fe814b14610a87578063cf73b2c81461087f578063cfe564c914610726578063d7f71ac31461052c578063d940ef70146104f5578063e0cc3eb6146103e9578063ea17532e14610336578063eb9f15be146102e0578063f2fde38b146102b55763ff2c58141461026e575f80fd5b346102b15760203660031901126102b1576004355f52600a60205260ff60405f205460881c165f52600c602052602061ffff60405f205460081c16604051908152f35b5f80fd5b346102b15760203660031901126102b1576102de6102d1613dc2565b6102d9614cb8565b61447a565b005b346102b15760203660031901126102b1576004356001600160401b0381116102b15760ff602061031581933690600401613d0a565b91908260405193849283378101600d81520301902054166040519015158152f35b346102b15760203660031901126102b1575f6040805161035581613e70565b82815282602082015201526004355f52600b60205260405f206040519061037b82613e70565b549064ffffffffff8216815260ff8260281c1691602082019060078410156103d5576103cc8260609564ffffffffff945283604086019360301c168352836040519551168552516020850190613f4f565b51166040820152f35b634e487b7160e01b5f52602160045260245ffd5b346102b15760203660031901126102b1576004355f52600a60205261ffff60405f20805490610498610429600361042260018501614332565b93016143f0565b9160405194846001600160401b038796168652818160401c166020870152818160501c16604087015264ffffffffff8160601c16606087015260ff8160881c16608087015260ff8160901c16151560a087015260981c1660c085015261012060e0850152610120840190613f5c565b828103610100840152602080835192838152019201905f5b8181106104be575050500390f35b825180516001600160a01b031685526020908101516001600160501b031681860152869550604090940193909201916001016104b0565b346102b15761050336613dd8565b905f52600a602052600260405f2001905f52602052602060ff60405f2054166040519015158152f35b346102b15761053a36613f80565b60015460405163e743862960e01b81523360048201526024810184905292939290602090829060449082906001600160a01b03165afa90811561071b575f916106ec575b50156106dd57815f52600a60205260405f205464ffffffffff8160601c16156106ce5760901c60ff166106bf576105b59082615486565b5f828152600b60205260409020805490929064ffffffffff166106b05764ffffffffff835460301c168061063c575b83546affffffffff0000000000001916603084901b69ffffffff0000000000001617845560408051848152602081018490527f5b4036fee23699edc6c33868b48eea7f82a415ae2715865db9ec6b74cc4701459190a1005b91908183146106a1577f5b4036fee23699edc6c33868b48eea7f82a415ae2715865db9ec6b74cc470145937f86ef48d3478dfc87f7a61b90ffdd3193d9862a68632a4c233e71d330b3bb5ce5604080958151908152846020820152a1935090916105e4565b63177366d160e01b5f5260045ffd5b6376cfa8c160e01b5f5260045ffd5b63142197d960e21b5f5260045ffd5b63d699f7e960e01b5f5260045ffd5b630a71f87f60e31b5f5260045ffd5b61070e915060203d602011610714575b6107068183613e8b565b810190613f9a565b8461057e565b503d6106fc565b6040513d5f823e3d90fd5b346102b15760603660031901126102b1576004356024356001600160401b0381116102b157610759903690600401613d0a565b60015460405163e743862960e01b815233600482015260448035602483018190529495939492602091839182906001600160a01b03165afa90811561071b575f91610860575b50156106dd57805f52600b6020528164ffffffffff60405f2054160361085157805f52600b60205260ff60405f205460281c1660078110156103d5576005116108425760c88311610833576108287f0ff410db68b95933b3297828599ba335d9c49f904355784c6a2bbbe839541dbc9460405194859485526060602086015260608501916143d0565b9060408301520390a1005b6347e8b47560e11b5f5260045ffd5b63d52ab5d160e01b5f5260045ffd5b6325d7e13960e11b5f5260045ffd5b610879915060203d602011610714576107068183613e8b565b8561079f565b346102b15761088d36613e1e565b60015460405163e743862960e01b8152336004820152602481018390529194929190602090829060449082906001600160a01b03165afa90811561071b575f91610a68575b50156106dd57835f52600b6020528164ffffffffff60405f2054160361085157835f52600b60205260ff60405f205460281c1660078110156103d55760021161084257815f52600a60205260405f205460ff8160881c165f52600c60205261ffff610946838260405f209460501c166140fe565b915460081c1610610a595763ffffffff82165f5b82811061099a5750507fac65b29e315dbc954a8da88cc0a5b5c6a59d3223452c29b42b5db6494a4f7d26936109959160405194859485614153565b0390a1005b6109a581848761410b565b35845f52600a6020526109f760405f20825f526002810160205260405f2060ff1981541690556109dc61ffff825460501c166153d0565b61ffff60501b82549160501b169061ffff60501b1916179055565b5f52600b60205260405f20908464ffffffffff835460301c1603610a4a5781546affffffffffff00000000001984166affffffffffffffffffffff1990911617650100000000001790915560010161095a565b63637df6ff60e11b5f5260045ffd5b632b229f6b60e11b5f5260045ffd5b610a81915060203d602011610714576107068183613e8b565b856108d2565b346102b157610a9536613dd8565b600354604051627eeac760e11b81523360048201526024810183905290602090829060449082906001600160a01b03165afa90811561071b575f91610b36575b5015610b2757805f52600b6020528164ffffffffff60405f2054160361085157815f52600a6020526001600160401b0360405f205416610b18576102de91615054565b6315d7cf2960e31b5f5260045ffd5b63078eec1360e11b5f5260045ffd5b90506020813d602011610b60575b81610b5160209383613e8b565b810103126102b1575183610ad5565b3d9150610b44565b346102b1575f3660031901126102b15760206001600160501b0360045460a01c16604051908152f35b346102b15760403660031901126102b1576004356001600160401b0381116102b157610bc1903690600401613dee565b600354604051627eeac760e11b815233600482015260248035908201819052939291602090829060449082906001600160a01b03165afa90811561071b575f91610cd5575b5015610b27578015610cc7575f5b818110610c6057507f284330b7aeacaa076942744a635969b490cf5cafa294d5b6c311f387078afa2892610c5560405193849360408552604085019161412f565b9060208301520390a1005b610c6b81838561410b565b3590815f52600a602052600260405f2001855f5260205260ff60405f20541615610cb8576001915f52600a602052600260405f2001855f5260205260405f2060ff19815416905501610c14565b6362bfd17560e11b5f5260045ffd5b6254c4fb60e71b5f5260045ffd5b90506020813d602011610cff575b81610cf060209383613e8b565b810103126102b1575184610c06565b3d9150610ce3565b346102b15760403660031901126102b1576004356001600160401b0381116102b157610d37903690600401613dee565b610d3f613d87565b610d47614cb8565b151560ff165f5b828110610d5757005b806001600160a01b03610d75610d70600194878961410b565b61419d565b165f52600f60205260405f208360ff1982541617905501610d4e565b346102b15760203660031901126102b1576004355f52600a602052602061ffff60405f205460981c16604051908152f35b346102b15760203660031901126102b1576004356001600160501b038116908181036102b1577f7934a8db3f8f75ea0c15b07050e7abbb51f58cfa3914f76450dc170f3d40d81191602091610e15614cb8565b6004805469ffffffffffffffffffff60a01b191660a09290921b69ffffffffffffffffffff60a01b16919091179055604051908152a1005b346102b15760603660031901126102b157602435600435610e6c613d46565b600354604051627eeac760e11b81523360048201526024810185905291929190602090829060449082906001600160a01b03165afa90811561071b575f91610fbd575b5015610b2757805f52600a60205260ff60405f205460881c165f52600c60205260405f205460ff81169081156106ce5760ff841691821115610fae57610ef4826144f8565b815f52600c6020526001600160501b038060405f205460681c169160681c169003936001600160501b038511610f9a57610f87606094610f5d6001600160501b037ff926d4ef55149e1fe1f7e953b8bddc590c485497a4f01d73ea589b8150bfed839816614b19565b5f858152600a60205260409020805460ff60881b191660889290921b60ff60881b16919091179055565b60405192835260208301526040820152a1005b634e487b7160e01b5f52601160045260245ffd5b63c59efceb60e01b5f5260045ffd5b90506020813d602011610fe7575b81610fd860209383613e8b565b810103126102b1575184610eaf565b3d9150610fcb565b346102b1575f3660031901126102b157611036604051611010604082613e8b565b60058152640352e302e360dc1b6020820152604051918291602083526020830190613f5c565b0390f35b346102b15760203660031901126102b1576001600160a01b0361105b613dc2565b611063614cb8565b166001600160601b0360a01b60135416176013555f80f35b346102b15760203660031901126102b15760206110ac6004355f52600b60205264ffffffffff60405f205416151590565b6040519015158152f35b613d56565b346102b15760603660031901126102b1576004356110d7613d87565b600354604051627eeac760e11b815233600482015260448035602483018190529492602091839182906001600160a01b03165afa90811561071b575f916111c5575b5015610b2757825f52600b6020528064ffffffffff60405f2054160361085157825f52600b60205260ff60405f205460281c1660078110156103d557600211610842575f818152600a60205260409020805460ff60901b1916831560901b60ff60901b161790557fdef22600eb997387bdb5a7edea2add06b5e92c0fda852986932731166349355192610995906040519384938460409194939260608201958252151560208201520152565b90506020813d6020116111ef575b816111e060209383613e8b565b810103126102b1575184611119565b3d91506111d3565b346102b15760803660031901126102b15760443560043560243560078310156102b157600354604051627eeac760e11b8152336004820152606435602482018190529391602090829060449082906001600160a01b03165afa90811561071b575f9161136c575b5015610b2757815f52600b6020528064ffffffffff60405f2054160361085157815f52600b60205260ff60405f205460281c1693835f52600b60205260ff60405f205460281c166007811015806103d5578282111561135e5784860361133f5760078710156103d5576103d5578511611330575b60078510156103d557808514611321578085111561131557806112fa57506102de9350614ceb565b919060066102de9503614fdb57611310816153e3565b614fdb565b91906102de9450614fdb565b635f5db10960e11b5f5260045ffd5b63bea1b71360e01b5f5260045ffd5b5060078610156103d55785106112d257631b1613f760e01b5f5260045ffd5b624da49d60e71b5f5260045ffd5b90506020813d602011611396575b8161138760209383613e8b565b810103126102b157518561125e565b3d915061137a565b346102b1576113ac36613dd8565b600354604051627eeac760e11b81523360048201526024810183905290602090829060449082906001600160a01b03165afa90811561071b575f91611441575b5015610b27577f86ef48d3478dfc87f7a61b90ffdd3193d9862a68632a4c233e71d330b3bb5ce591816040925f52600b602052825f206affffffffff00000000000019815416905582519182526020820152a1005b90506020813d60201161146b575b8161145c60209383613e8b565b810103126102b15751836113ec565b3d915061144f565b346102b15761148136613f80565b60015460405163e743862960e01b81523360048201526024810184905291939190602090829060449082906001600160a01b03165afa90811561071b575f916115d4575b50156106dd57805f52600a60205260405f20916002830193815f528460205260ff60405f20541615610cb85761150d825f52600b60205264ffffffffff60405f205416151590565b6106b05761151b9083615486565b825460ff8160881c165f52600c60205261ffff8060405f205460081c169160501c161015610a595761158d6040937f4029968a3f2d34f7c8df00f90946159de86b935a5c57f786888ce5d1d7845d9c95835f52602052845f2060ff1981541690556109dc61ffff825460501c166153d0565b5f818152600b60209081529084902080546affffffffff0000000000001965ffffffffffff1990911663ffffffff86161765010000000000171690558351928352820152a1005b6115ed915060203d602011610714576107068183613e8b565b846114c5565b346102b1575f3660031901126102b15760206001600160a01b035f5160206156825f395f51905f525416604051908152f35b346102b15761163336613efd565b9061163c614cb8565b5f5b828110611676576040517feda6ff97e5243629083aff63c9a0f9a5ad50c6d235c74b51e9ec92dc6ad5f6db9080610995868683614247565b60ff61168b611686838686614229565b614239565b161515806116c4575b156116b557806116af6116aa6001938686614229565b6151c1565b0161163e565b632520e1bd60e11b5f5260045ffd5b5060ff6116d5611686838686614229565b165f52600c60205260ff60405f20541615611694565b346102b1576116f936613e1e565b600354604051627eeac760e11b81523360048201526024810183905291929190602090829060449082906001600160a01b03165afa90811561071b575f91611816575b5015610b2757815f52600b6020528364ffffffffff60405f2054160361085157815f52600b60205260ff60405f205460281c1660078110156103d557600211610842578015611807575f5b8181106117c257507f5ceebf1e578529395e97c10d2075cccca96caf69041edf3d24f9a95bf42b696893916109959160405194859485614153565b6117cd81838661410b565b355f52600b60205260405f20908564ffffffffff835460301c1603610a4a5781546affffffffff0000000000001916909155600101611787565b63d411ab5760e01b5f5260045ffd5b90506020813d602011611840575b8161183160209383613e8b565b810103126102b157518561173c565b3d9150611824565b346102b15760203660031901126102b1576004355f52600b60205264ffffffffff60405f2054165f52600a602052611036611888600160405f2001614332565b604051918291602083526020830190613f5c565b346102b15760803660031901126102b1576004356024356001600160401b0381116102b1576118cf903690600401613dee565b916044356001600160401b0381116102b1576118ef903690600401613dee565b600354604051627eeac760e11b8152336004820152606435602482018190529396929591602090829060449082906001600160a01b03165afa90811561071b575f91611ad2575b5015610b27575f5b82811061194757005b61195281848461410b565b359061195f81888a61410b565b359160078310156102b157600354604051627eeac760e11b81523360048201526024810188905290602090829060449082906001600160a01b03165afa90811561071b575f91611aa1575b5015610b2757805f52600b6020528664ffffffffff60405f2054160361085157805f52600b60205260ff60405f205460281c16865f52600b60205260ff60405f205460281c166007811015806103d5578582111561135e57838903611a825760078310156103d5576103d5578111611330575b60078110156103d55783811461132157600193879181811115611a775781611a51575050611a4b9188614ceb565b0161193e565b926006611a649414611a69575b89614fdb565b611a4b565b611a728a6153e3565b611a5e565b50611a649289614fdb565b5060078210156103d5578110611a1d57631b1613f760e01b5f5260045ffd5b90506020813d8211611aca575b81611abb60209383613e8b565b810103126102b157518a6119aa565b3d9150611aae565b90506020813d602011611afc575b81611aed60209383613e8b565b810103126102b1575187611936565b3d9150611ae0565b346102b1575f3660031901126102b157611b1c614cb8565b5f6001600160a01b035f5160206156825f395f51905f52546001600160601b0360a01b81165f5160206156825f395f51905f5255167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346102b1576020611b90611b8a36613dd8565b906142ff565b611b9d6040518092613f4f565bf35b346102b1575f3660031901126102b1576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163003611c095760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b63703e46dd60e11b5f5260045ffd5b346102b15760203660031901126102b15760043561ffff8116908181036102b1577fbb4df7a4ea901e618bc10245eee20e4c8522555bfe10816b8128332bc0e334a991602091611c66614cb8565b6003805461ffff60c81b191660c89290921b61ffff60c81b16919091179055604051908152a1005b346102b157611c9c36613efd565b611ca4614cb8565b5f5b818110611cdf57507fab0ba102ddeb1d896e289b4c68719d8a1f4a198be3ab6883484a1fe929718a4f9161099560405192839283614247565b80611cfb60ff611cf56116866001958789614229565b166144f8565b611d096116aa828587614229565b01611ca6565b60403660031901126102b157611d23613dc2565b6024356001600160401b0381116102b157366023820112156102b157611d53903690602481600401359101613ec7565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016803014908115611f18575b50611c0957611d95614cb8565b6040516352d1902d60e01b81526001600160a01b0383169290602081600481875afa5f9181611ee4575b50611dd75783634c9c8ce360e01b5f5260045260245ffd5b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc859203611ed25750813b15611ec0577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b031916821790557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a2815115611ea8575f808360206102de95519101845af43d15611ea0573d91611e8483613eac565b92611e926040519485613e8b565b83523d5f602085013e615627565b606091615627565b505034611eb157005b63b398979f60e01b5f5260045ffd5b634c9c8ce360e01b5f5260045260245ffd5b632a87526960e21b5f5260045260245ffd5b9091506020813d602011611f10575b81611f0060209383613e8b565b810103126102b157519085611dbf565b3d9150611ef3565b90506001600160a01b037f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416141583611d88565b346102b15760203660031901126102b1575f60a0604051611f6d81613e55565b82815282602082015282604082015282606082015282608082015201526004355f52600c60205260c060405f206001600160501b0360405191611faf83613e55565b5464ffffffffff60ff82169384815262ffffff6020820161ffff8560081c16815261ffff60408401818760181c168152816060860193858960281c1685528960a060808901988a8c60401c168a52019960681c1689526040519a8b52511660208a0152511660408801525116606086015251166080840152511660a0820152f35b346102b157602061204036613dd8565b5f52600b825264ffffffffff60405f20541614604051908152f35b346102b15760403660031901126102b15760243561ffff811681036102b1576001600160a01b036008541633036120bb576004355f908152600a60205260409020805461ffff60981b191660989290921b61ffff60981b16919091179055005b63993eecd960e01b5f5260045ffd5b346102b15760203660031901126102b1576004355f52600b60205264ffffffffff60405f2054165f52600a602052602060ff60405f205460881c16604051908152f35b346102b15761211b36613e1e565b600354604051627eeac760e11b81523360048201526024810183905292949290602090829060449082906001600160a01b03165afa90811561071b575f91612231575b5015610b2757805f52600b6020528164ffffffffff60405f2054160361085157805f52600b60205260ff60405f205460281c1660078110156103d55760021161084257815f52600a60205260405f20938015610cc75760025f9501945b8181106121f5576040517f473f1c3e2a696b3007a52e476d439236942f08e0cff330fc24aeefab41d0d98e908061099586868a8a85614153565b61220081838761410b565b3590815f528660205260ff60405f20541615610cb8576001915f528660205260405f2060ff198154169055016121bb565b90506020813d60201161225b575b8161224c60209383613e8b565b810103126102b157518561215e565b3d915061223f565b346102b15760603660031901126102b1576024356004356001600160401b0382116102b157366023830112156102b1578160040135906001600160401b0382116102b1576024830192602436918460061b0101116102b15760015460405163e743862960e01b815233600482015260448035602483018190529392602091839182906001600160a01b03165afa90811561071b575f91612688575b50156106dd57815f52600b6020528064ffffffffff60405f2054160361085157815f52600b60205260ff60405f205460281c1660078110156103d55760051161084257600583116126795761235283614176565b926123606040519485613e8b565b80845261236c81614176565b602085019590601f19013687376001600160a01b03600654165f5b8381106124ec575050825f52600a602052600360405f2001906801000000000000000083116124d85781548383558084106124b2575b50905f5260205f205f915b8383106124465750505050604051926060840191845260606020850152518091526080830193905f5b818110612427577f72efcd8f70847517ad3bc1486f78c9e8353718305008d04c90e86e8eacea2bdc8580888760408301520390a1005b82516001600160a01b03168652602095860195909201916001016123f1565b60016040826001600160a01b0361245d849561419d565b86546001600160a01b031916911617855561247a602082016141b1565b855469ffffffffffffffffffff60a01b191660a09190911b69ffffffffffffffffffff60a01b161785559290940193920191016123c8565b825f528360205f2091820191015b8181106124cd57506123bd565b5f81556001016124c0565b634e487b7160e01b5f52604160045260245ffd5b6124fa610d7082868661418d565b906001600160a01b0360405192633af32abf60e01b84521691826004820152602081602481875afa90811561071b575f9161265b575b501561264c576001600160501b03612554602061254e84898961418d565b016141b1565b166102d181036125d857506040516301ffc9a760e01b81526380ac58cd60e01b6004820152602081602481865afa90811561071b575f916125ba575b50156125ab576001915b6125a4828a614202565b5201612387565b63adc1f5ed60e01b5f5260045ffd5b6125d2915060203d8111610714576107068183613e8b565b8a612590565b6104830361263d576040516301ffc9a760e01b8152636cdb3d1360e11b6004820152602081602481865afa90811561071b575f9161261f575b50156125ab5760019161259a565b612637915060203d8111610714576107068183613e8b565b8a612611565b630c1516e360e31b5f5260045ffd5b63f8f94f4960e01b5f5260045ffd5b612673915060203d8111610714576107068183613e8b565b8a612530565b636e58b71560e01b5f5260045ffd5b6126a1915060203d602011610714576107068183613e8b565b856122fe565b346102b1576126b536613e1e565b600354604051627eeac760e11b81523360048201526024810183905291929190602090829060449082906001600160a01b03165afa90811561071b575f9161280e575b5015610b2757815f52600b6020528364ffffffffff60405f2054160361085157815f52600b60205260ff60405f205460281c1660078110156103d55760021161084257835f52600a60205260405f205460ff8160881c165f52600c60205261ffff61276c838260405f209460501c166140fe565b915460081c1610610a59575f5b8181106127b457507f5b1a562619687202d271aec5b478cdde4e6e135bbdec7a5f7a11bf3056b5342693916109959160405194859485614153565b6127bf81838661410b565b3590855f52600a602052600260405f2001825f528060205260ff60405f2054166127ff576001925f5260205260405f208260ff1982541617905501612779565b63cbc57df560e01b5f5260045ffd5b90506020813d602011612838575b8161282960209383613e8b565b810103126102b15751856126f8565b3d915061281c565b346102b15761284e36613dd8565b90815f52600b60205264ffffffffff60405f2054161480612877575b6020906040519015158152f35b505f52600b60205260ff60405f205460281c1660078110156103d5576004602091101561286a565b346102b15760203660031901126102b157602460206001600160a01b036002541660405192838092631e7bbfab60e01b825260043560048301525afa801561071b576020915f916128fd575b506001600160a01b0360405191168152f35b61291d9150823d8411612923575b6129158183613e8b565b81019061400a565b826128eb565b503d61290b565b346102b1576101803660031901126102b157612944613dc2565b6064356001600160401b0381116102b157612963903690600401613d0a565b91906084356001600160401b0381116102b157612984903690600401613d0a565b60a4949194356001600160401b0381116102b1576129a6903690600401613d0a565b60c4969196356001600160401b0381116102b1576129c8903690600401613d0a565b939092610164359687151588036102b1576001600160a01b03601254163303612d89576044355f908152600a60205260409020805471ff000000000000000000ffffffffffffffff19166024356001600160401b0316176101243560881b60ff60881b1617815596875461ffff60401b191660e43560401b61ffff60401b16178855875466ffffffffffffff60501b19166101043560601b64ffffffffff60601b1617600160501b178855875461ffff60981b19166101443560981b61ffff60981b161788556024355f908152600b60205260409020805465ffffffffffff191660443563ffffffff1617650600000000001790556001600160401b0381116124d857612ae581612adc60018b015461404c565b60018b0161409a565b5f601f8211600114612ce25795899791955f9c7fbd0ab160e05d3247ed89c8619b640c22634a65d36949c146e3133fc319ccfb4d96612bac968f60209f9d9b97612c469f9b8d6001612b538380612b899e612b849891612cd7575b508160011b915f199060031b1c19161790565b9101555b612b72612b6d612b68368486613ec7565b6150f3565b6140df565b805460ff191660011790553691613ec7565b614a79565b60405191829160443583526024358b84015260c0604084015260c0830190613fb2565b60e43560608301526101243560808301526101043560a08301520390a1612c6e575b50507fd0f236da2a55211dd4cf5df9ab238a0858e8d127f01ce8d39d4299657e6d10c86040805160443581526101443586820152a16001600160a01b036002541690604051948580948193638471af7760e01b83526044359060048401602090939291936001600160a01b0360408201951681520152565b03925af1801561071b57612c5657005b6102de9060203d602011612923576129158183613e8b565b805460ff60901b191682151560901b60ff60901b161790557fdef22600eb997387bdb5a7edea2add06b5e92c0fda852986932731166349355190612ccd9060408051604435815291156020830152602435908201529081906060820190565b0390a18480612bce565b90508601355f612b40565b600189015f5260205f20905f5b601f1984168110612d715750955f9c7fbd0ab160e05d3247ed89c8619b640c22634a65d36949c146e3133fc319ccfb4d96612bac96612b8460209f9d9b9597612c469f9d97612b89998f9d82601f19811610612d58575b50508d60018083811b01910155612b57565b8301355f19600384901b60f8161c191690555f80612d46565b90916020600181928588013581550193019101612cef565b637fea9dc560e01b5f5260045ffd5b346102b1576101403660031901126102b1576004356001600160a01b0381168091036102b1576024356001600160a01b0381168091036102b157612dda613d96565b91612de3613dac565b926084356001600160501b038116938482036102b15760a435926001600160a01b0384168094036102b15760c4359361ffff8516928386036102b15760e4359564ffffffffff871687036102b15761010435976001600160a01b0389168099036102b15761012435996001600160a01b038b16809b036102b1575f5160206156a25f395f51905f52549060ff8260401c16159c8d6001600160401b0384168015918261311c575b506001149081613112575b159081613109575b506130fa577fbb4df7a4ea901e618bc10245eee20e4c8522555bfe10816b8128332bc0e334a99a60209a8f958c957f7934a8db3f8f75ea0c15b07050e7abbb51f58cfa3914f76450dc170f3d40d8119760016001600160401b03198316175f5160206156a25f395f51905f52556130ce575b50612f1861545b565b612f2061545b565b612f293361447a565b612f3161545b565b6001600160601b0360a01b5f5416175f556001600160a01b036003549a16916001600160a01b0360045494846001600160601b0360a01b871617600455166001600160601b0360a01b600554161760055564ffffffffff60a01b9060a01b1698898966ffffffffffffff60c81b8d1617176003556001600160601b0360a01b6006541617600655612fc0614cb8565b7fffff0000000000000000000000000000000000000000000000000000000000009092161760a09190911b69ffffffffffffffffffff60a01b1617600455604051908152a161300d614cb8565b7fffffffffff000000000000000000000000000000000000000000000000000000909316171760c89190911b61ffff60c81b1617600355604051908152a16001600160601b0360a01b60125416176012556001600160601b0360a01b601354161760135561307757005b68ff0000000000000000195f5160206156a25f395f51905f5254165f5160206156a25f395f51905f52557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a1005b68ffffffffffffffffff191668010000000000000001175f5160206156a25f395f51905f52555f612f0f565b63f92ee8a960e01b5f5260045ffd5b9050158f612e9d565b303b159150612e95565b91508f612e8a565b346102b15760203660031901126102b1576004355f52600a60205260ff60405f205460881c165f52600c602052602061ffff60405f205460181c16604051908152f35b346102b15760a03660031901126102b1576004356001600160a01b0381168091036102b1576024356001600160a01b0381168091036102b1576131a8613d96565b916131b1613dac565b90608435936001600160a01b0385168095036102b1576131cf614cb8565b600254936001600160a01b03851681811591821561326b575b50501561325c576001600160a01b039485936001600160601b0360a01b60015416176001556001600160601b0360a01b1617600255166001600160601b0360a01b6007541617600755166001600160601b0360a01b60085416176008556001600160601b0360a01b60095416176009555f80f35b6337b8bea960e21b5f5260045ffd5b14905081886131e8565b346102b15760e03660031901126102b1576004356024356001600160401b0381116102b1576132a8903690600401613d0a565b90916044356001600160401b0381116102b1576132c9903690600401613d0a565b9290936064356001600160401b0381116102b1576132eb903690600401613d0a565b6084939193356001600160401b0381116102b15761330d903690600401613d0a565b60015460405163e743862960e01b815233600482015260c435602482018190529960a4359893959392909190602090829060449082906001600160a01b03165afa90811561071b575f9161345a575b50156106dd57895f52600b6020528864ffffffffff60405f2054160361085157895f52600b60205260ff60405f205460281c1660078110156103d557600511610842577fff9c4aa2c485437fd53cb8c1462dd050bd76f4e240718699dd1bb1bb248c6be49a61340261341597613433998c5f52600a60205260ff60405f205460881c165f52600c6020526133fc62ffffff60405f205460281c168d61451d565b8c61454b565b61343e575b612b848787878787876148a1565b60405194859485526020850152608060408501526080840190613fb2565b9060608301520390a1005b6134556001600160501b0360045460a01c16614b19565b613407565b613473915060203d602011610714576107068183613e8b565b8c61335c565b346102b15760603660031901126102b15760443560243560043560078310156102b1575f818152600a6020908152604091829020546003549251627eeac760e11b81523360048201526001600160401b03909116602482015291829060449082906001600160a01b03165afa90811561071b575f91613581575b5015610b2757815f52600b6020528064ffffffffff60405f2054160361085157805f52600a6020526001600160401b0360405f20541692838314613572578015613563576006811015613554578361354f916102de9584614fdb565b615054565b63cbcd7cd360e01b5f5260045ffd5b5061354f836102de9483614ceb565b633493d67f60e11b5f5260045ffd5b90506020813d6020116135ab575b8161359c60209383613e8b565b810103126102b15751846134f3565b3d915061358f565b346102b15760603660031901126102b1576004356024359064ffffffffff82168092036102b157604435908115158092036102b157335f52600f60205260ff60405f2054161561367c57805f52600a60205260405f20918254938064ffffffffff8660a81c16019364ffffffffff8511610f9a577ff34537c7169eedc3a5bf2996afbe8ee8754382962b6a4276ee7d450e49748a0f9560609564ffffffffff60a81b9060a81b169064ffffffffff60a81b191617905560405192835260208301526040820152a1005b63e5e9973760e01b5f5260045ffd5b346102b15760603660031901126102b15760043560ff8116908181036102b1576024359160ff8316928381036102b1576136c3613d46565b6136cb614cb8565b606460ff6136e2836136dd8689614038565b614038565b1603613754577fb4ae64ee51c0d84410a16847b3f8f3d7ac7bbbeaf2abae8671beba816f0279e39460609460ff936005548560b01b8560b01b16928660a01b9060a01b169062ffffff60a01b191617908560a81b9060a81b1617176005556040519384526020840152166040820152a1005b63196f075b60e11b5f5260045ffd5b346102b15760e03660031901126102b157600435906024356001600160401b0381116102b157613797903690600401613d0a565b916044356001600160401b0381116102b1576137b7903690600401613d0a565b6064949194356001600160401b0381116102b1576137d9903690600401613d0a565b6084929192356001600160401b0381116102b1576137fb903690600401613d0a565b93909260a4359861ffff8a1696878b036102b15760c4359960ff8b16998a8c036102b15760015463e743862960e01b8252336004830152602482018f9052602090829060449082906001600160a01b03165afa90811561071b575f91613ceb575b50156106dd5761387e8d5f52600b60205264ffffffffff60405f205416151590565b6106b057895f52600c60205260405f209b6138988b6144f8565b8c5460281c62ffffff166138ac908b61451d565b6003548060a01c64ffffffffff169c64ffffffffff8e14610f9a578f90613999908f64ffffffffff95613952918760a01b6001820160a01b168860a01b198816176003555f52600a60205261393660405f20936001600160401b03808816166001600160401b0319865416178555849081549060ff60881b9060881b169060ff60881b1916179055565b825461ffff60401b191660409190911b61ffff60401b16178255565b805466ffffffffffffff60501b191642861660601b64ffffffffff60601b1617600160501b178155600354815461ffff60981b191660309190911c61ffff60981b16179055565b5f52600b6020528163ffffffff60405f209260a01c161682198254161781556506000000000065ff0000000000198254161781555460301c168d81613bba575b505095613a5495613a729a956001600160501b039a95613a33955f9f9b958f907fbd0ab160e05d3247ed89c8619b640c22634a65d36949c146e3133fc319ccfb4d9c613a249261454b565b50612b848787878787876148a1565b916040519384938a85528c602086015260c0604086015260c0850190613fb2565b91606084015260808301524260a08301520390a15460681c16614b19565b601354600254604051638471af7760e01b8152336004820152602481019390935291926001600160a01b039182169260209285926044928492165af191821561071b575f92613b99575b5060206001600160a01b03600154169360246040518096819363231b667d60e21b835260048301525afa92831561071b575f93613b67575b5060846020925f6001600160a01b0393604051968795869463ec01f01960e01b86526013600487015216602485015215156044840152600160648401525af1801561071b57613b3f57005b6102de9060203d602011613b60575b613b588183613e8b565b810190614029565b503d613b4e565b6001600160a01b039193506020925f613b8e608493863d8811610714576107068183613e8b565b959350509250613af4565b613bb391925060203d602011612923576129158183613e8b565b9083613abc565b600354604051627eeac760e11b81523360048201526024810192909252969d959b949a9599939892979496919491602090829060449082906001600160a01b03165afa90811561071b575f91613cb9575b5015610b27575f9c613a729b8f9b8f8f9b613a549b6001600160501b039f9d7f86ef48d3478dfc87f7a61b90ffdd3193d9862a68632a4c233e71d330b3bb5ce57fbd0ab160e05d3247ed89c8619b640c22634a65d36949c146e3133fc319ccfb4d9f613a339c81604092613a249852600b6020525f8390206affffffffff00000000000019815416905582519182526020820152a150969c50969c9e50509550959a50959a9c50958d6139d9565b90506020813d602011613ce3575b81613cd460209383613e8b565b810103126102b157518f613c0b565b3d9150613cc7565b613d04915060203d602011610714576107068183613e8b565b8e61385c565b9181601f840112156102b1578235916001600160401b0383116102b157602083818601950101116102b157565b359061ffff821682036102b157565b6044359060ff821682036102b157565b346102b15760203660031901126102b1576004355f52600b602052602064ffffffffff60405f205416604051908152f35b6024359081151582036102b157565b604435906001600160a01b03821682036102b157565b606435906001600160a01b03821682036102b157565b600435906001600160a01b03821682036102b157565b60409060031901126102b1576004359060243590565b9181601f840112156102b1578235916001600160401b0383116102b1576020808501948460051b0101116102b157565b9060606003198301126102b15760043591602435906001600160401b0382116102b157613e4d91600401613dee565b909160443590565b60c081019081106001600160401b038211176124d857604052565b606081019081106001600160401b038211176124d857604052565b90601f801991011681019081106001600160401b038211176124d857604052565b6001600160401b0381116124d857601f01601f191660200190565b929192613ed382613eac565b91613ee16040519384613e8b565b8294818452818301116102b1578281602093845f960137010152565b9060206003198301126102b1576004356001600160401b0381116102b157826023820112156102b1578060040135926001600160401b0384116102b157602460c08502830101116102b1576024019190565b9060078210156103d55752565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b60609060031901126102b157600435906024359060443590565b908160209103126102b1575180151581036102b15790565b9080602083519182815201916020808360051b8301019401925f915b838310613fdd57505050505090565b9091929394602080613ffb600193601f198682030187528951613f5c565b97019301930191939290613fce565b908160209103126102b157516001600160a01b03811681036102b15790565b908160209103126102b1575190565b9060ff8091169116019060ff8211610f9a57565b90600182811c9216801561407a575b602083101461406657565b634e487b7160e01b5f52602260045260245ffd5b91607f169161405b565b81811061408f575050565b5f8155600101614084565b9190601f81116140a957505050565b6140d3925f5260205f20906020601f840160051c830193106140d5575b601f0160051c0190614084565b565b90915081906140c6565b60208091604051928184925191829101835e8101600d81520301902090565b91908201809211610f9a57565b919081101561411b5760051b0190565b634e487b7160e01b5f52603260045260245ffd5b81835290916001600160fb1b0383116102b15760209260051b809284830137010190565b9493929160409261417192875260606020880152606087019161412f565b930152565b6001600160401b0381116124d85760051b60200190565b919081101561411b5760061b0190565b356001600160a01b03811681036102b15790565b356001600160501b03811681036102b15790565b80511561411b5760200190565b80516001101561411b5760400190565b80516002101561411b5760600190565b80516003101561411b5760800190565b805182101561411b5760209160051b010190565b81810292918115918404141715610f9a57565b919081101561411b5760c0020190565b3560ff811681036102b15790565b60208082528101839052604001915f905b8082106142655750505090565b909192833560ff81168091036102b157815261ffff61428660208601613d37565b16602082015261ffff61429b60408601613d37565b166040820152606084013562ffffff81168091036102b1576060820152608084013564ffffffffff81168091036102b157608082015260a0840135906001600160501b0382168092036102b15760c08160019360a083940152019401920190614258565b815f52600b60205264ffffffffff60405f2054161461431d57505f90565b5f52600b60205260ff60405f205460281c1690565b9060405191825f8254926143458461404c565b80845293600181169081156143ae575060011461436a575b506140d392500383613e8b565b90505f9291925260205f20905f915b8183106143925750509060206140d3928201015f61435d565b6020919350806001915483858901015201910190918492614379565b9050602092506140d394915060ff191682840152151560051b8201015f61435d565b908060209392818452848401375f828201840152601f01601f1916010190565b908154916143fd83614176565b9261440b6040519485613e8b565b80845260208401915f5260205f20915f905b82821061442a5750505050565b604051604081018181106001600160401b038211176124d85760019283926020926040526001600160501b0388546001600160a01b038116835260a01c168382015281520194019101909261441d565b6001600160a01b031680156144e5576001600160a01b035f5160206156825f395f51905f5254826001600160601b0360a01b8216175f5160206156825f395f51905f5255167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3565b631e4fbdf760e01b5f525f60045260245ffd5b5f52600c60205260ff60405f2054161561450e57565b630bec67bf60e31b5f5260045ffd5b908115159182614540575b50501561453157565b6305615c0b60e11b5f5260045ffd5b111590505f80614528565b9192905f61458c9173e3223eaf0e260b54a8ce777ac9f4a972310370c095604051938492839263b2dad15560e01b84526020600485015260248401916143d0565b0381875af490811561071b575f91614828575b508093600382511061481957601482511161480a57602060405180926391ae2cdb60e01b825282600483015281806145da6024820188613f5c565b03915af490811561071b575f916147eb575b50156147dc576145fb816150f3565b925f52600a602052600160405f200190614617612b6883614332565b9360405194602086018151966146476020828186019a808c875e81015f838201520301601f198101835282613e8b565b51902091604051926020840182519461467a60208281870198808a875e81015f838201520301601f198101835282613e8b565b5190201415968761468e575b505050505050565b60ff60405160208185518089835e8101600d81520301902054166147cd576020925190816147a9575b5050604051928391518091835e8101600d815203019020600160ff198254161790558051906001600160401b0382116124d8576146fe826146f8855461404c565b8561409a565b602090601f83116001146147465761472d92915f918361473b575b50508160011b915f199060031b1c19161790565b90555b5f8080808080614686565b015190505f80614719565b90601f19831691845f52815f20925f5b8181106147915750908460019594939210614779575b505050811b019055614730565b01515f1960f88460031b161c191690555f808061476c565b92936020600181928786015181550195019301614756565b839082604051938492835e8101600d81520301902060ff1981541690555f806146b7565b633480a50360e21b5f5260045ffd5b634caa0cad60e11b5f5260045ffd5b614804915060203d602011610714576107068183613e8b565b5f6145ec565b63680b6caf60e01b5f5260045ffd5b6365ac5e6760e11b5f5260045ffd5b90503d805f833e6148398183613e8b565b8101906020818303126102b1578051906001600160401b0382116102b1570181601f820112156102b15780519061486f82613eac565b9261487d6040519485613e8b565b828452602083830101116102b157815f9260208093018386015e830101525f61459f565b949392909460198611614a6a5785158015614a5f575b15614a505760206148fa9173e3223eaf0e260b54a8ce777ac9f4a972310370c09760405193849283926309f241ed60e41b845285600485015260248401916143d0565b0381895af490811561071b575f91614a31575b5015614a225760198111614a13576149449160209160405193849283926309f241ed60e41b845285600485015260248401916143d0565b0381875af490811561071b575f916149f4575b50156149e657601982116149d75761498f926020926040518095819482936309f241ed60e41b845287600485015260248401916143d0565b03915af490811561071b575f916149b8575b50156149a957565b6388d04d1f60e01b5f5260045ffd5b6149d1915060203d602011610714576107068183613e8b565b5f6149a1565b6348997f7160e01b5f5260045ffd5b620f7f1b60e91b5f5260045ffd5b614a0d915060203d602011610714576107068183613e8b565b5f614957565b6304121ae360e31b5f5260045ffd5b63088cc68560e21b5f5260045ffd5b614a4a915060203d602011610714576107068183613e8b565b5f61490d565b638c72c2a160e01b5f5260045ffd5b5060048610156148b7565b630da1592560e21b5f5260045ffd5b9695929194939060405195614a8f60a088613e8b565b6004875260805f5b818110614b0857505092614af69492614ad0614ae393899896614b059a9c614abe8b6141c5565b52614ac88a6141c5565b503691613ec7565b614ad9886141d2565b52614ac8876141d2565b614aec856141e2565b52614ac8846141e2565b614aff826141f2565b526141f2565b50565b806060602080938c01015201614a97565b5f906001600160a01b035f54166064614b3a60ff60055460a01c1684614216565b04813b156102b15760405163079cc67960e41b815233600482015260248101919091525f8160448183865af1801561071b57614ca3575b50614bcc60206001600160a01b03600454166064614b9760ff60055460a81c1687614216565b6040516323b872dd60e01b81523360048201526001600160a01b03909316602484015204604482015291829081906064820190565b038187865af18015614c985791849391602093614c469650614c7d575b506001600160a01b03846064614c096005549560ff8760b01c1690614216565b0493604051978895869485936323b872dd60e01b85521633600485016001600160a01b036040929594938160608401971683521660208201520152565b03925af1908115614c715750614c595750565b614b059060203d602011610714576107068183613e8b565b604051903d90823e3d90fd5b614c9390843d8611610714576107068183613e8b565b614be9565b6040513d86823e3d90fd5b614cb09193505f90613e8b565b5f915f614b71565b6001600160a01b035f5160206156825f395f51905f5254163303614cd857565b63118cdaa760e01b5f523360045260245ffd5b905f92825f52600a60205260405f20826001600160401b0382541614614fcd575b61ffff815460501c16908115610f9a5780546bffff0000000000000000000019165f1990920160501b61ffff60501b1691909117815561ffff905460501c16614f915750815f52600a60205260405f2061ffff815460501c16614f8257612b6d612b686001614d7b9301614332565b60ff198154169055815f52600a602052600360405f205f815560018101614da2815461404c565b9081614f3f575b5050018054905f815581614f21575b50507fe5cdc676962c829e36cbb7b0c60a322b60e2d01db199590aab156803c5a97ae66020604051848152a15b5f818152600b60205260409020805465ffffffffffff191690556007546001600160a01b0316803b156102b1575f8091604460405180948193637792156760e11b83528860048401528760248401525af1801561071b57614f0c575b506001600160a01b0360085416803b15614eef57838091604460405180948193637792156760e11b83528860048401528760248401525af18015614c9857908491614ef3575b50506001600160a01b036009541691823b15614eef579060448492836040519586948593637792156760e11b8552600485015260248401525af18015614ee457614ecf575050565b614eda828092613e8b565b614ee15750565b80fd5b6040513d84823e3d90fd5b8380fd5b81614efd91613e8b565b614f0857825f614e87565b8280fd5b614f199193505f90613e8b565b5f915f614e41565b5f5260205f20908101905b81811015614db8575f8155600101614f2c565b81601f5f9311600114614f565750555b5f80614da9565b81835260208320614f7291601f0160051c810190600101614084565b8082528160208120915555614f4f565b6370141ec960e11b5f5260045ffd5b60607fa21e607ac5445b7603f2e7e913bb2a66f680c289fb1a2ce735eb6c92713c5dd991604051908582528460208301526040820152a1614de5565b614fd6846153e3565b614d0c565b909192825f52600b60205260405f2060078510156103d5577f57ff30d4395dd2630a292b8a50feba238650ef9136dd5961908b899bb1ba9916946080948261504c935465ff00000000008460281b169065ff0000000000191617905560405194855260208501526040840190613f4f565b6060820152a1565b5f818152600a60209081526040808320805467ffffffffffffffff19166001600160401b038716179055600e8252808320805464ffffffffff19169055848352600b825291829020805465ff000000000019166506000000000017905581519283528201929092527fb36e6603e4ebbca03e98d06ae8a134eb0c59baf95557ee9403a9fe073b6ac0749190a1565b90815181101561411b570160200190565b90615121602080936040519481869251918291018484015e81015f838201520301601f198101845283613e8b565b5f5b8251811015614b055761513681846150e2565b51604160f81b6001600160f81b03198216908110159081615193575b50615161575b50600101615123565b60f81c6020019060ff8211610f9a5760019160f81b6001600160f81b0319165f1a61518c82866150e2565b5390615158565b602d60f91b101590505f615152565b3561ffff811681036102b15790565b3562ffffff811681036102b15790565b60ff6151cc82614239565b16600181116152af575b5f52600c60205260405f209060ff6151ed82614239565b1682549162ffff00615201602083016151a2565b60081b169264ffff000000615218604084016151a2565b60181b169067ffffff0000000000615232606085016151b1565b60281b169160808401359464ffffffffff861686036102b1576cffffffffff00000000000000009469ffffffffffffffffffff60681b906152759060a0016141b1565b60681b16966001600160501b0360681b199386199267ffffff0000000000199164ffffffffff1916171617161716179160401b1617179055565b5f198101818111610f9a575f52600c60205260405f20604051906152d282613e55565b549060ff821681526020810161ffff8360081c16815260a0604083019261ffff8560181c1684526001600160501b03606082019562ffffff8160281c16875264ffffffffff8160401c16608084015260681c1691829101526001600160501b0361533e60a088016141b1565b16106153c15761ffff80615354602088016151a2565b9251169116106153b25761ffff8061536e604087016151a2565b9251169116106153a35762ffffff80615389606086016151b1565b925116911610156151d657633f07a64560e01b5f5260045ffd5b6383202d3d60e01b5f5260045ffd5b637dc1f31b60e01b5f5260045ffd5b636dddf41160e11b5f5260045ffd5b61ffff1661ffff8114610f9a5760010190565b5f818152600a60209081526040808320805467ffffffffffffffff198116909155600e835292819020805464ffffffffff19164264ffffffffff1617905580519384526001600160401b0392909216908301527f81ec2aca54fed63234990c222e20c265fbd80545574659c8e359fe9dc3cda16291a1565b60ff5f5160206156a25f395f51905f525460401c161561547757565b631afcd79f60e31b5f5260045ffd5b5f52600a60205261549c600360405f20016143f0565b80516154a6575050565b5f92835b825185108061561f575b15615604576104836001600160501b0360206154d08887614202565b510151160361556a57506001600160a01b036154ec8584614202565b5151604051627eeac760e11b8152336004820152602481018690529160209183916044918391165afa90811561071b575f91615539575b501515935b5f198114610f9a57600101936154aa565b90506020813d8211615562575b8161555360209383613e8b565b810103126102b157515f615523565b3d9150615546565b936102d16001600160501b0360206155828487614202565b510151160361552857602460206001600160a01b036155a18487614202565b515116604051928380926331a9108f60e11b82528960048301525afa5f91816155e4575b506155d1575b50615528565b6001600160a01b0316331494505f6155cb565b6155fd91925060203d8111612923576129158183613e8b565b905f6155c5565b92509250501561561057565b63466e5a3d60e11b5f5260045ffd5b5080156154b4565b9061564b575080511561563c57805190602001fd5b63d6bda27560e01b5f5260045ffd5b81511580615678575b61565c575090565b6001600160a01b0390639996b31560e01b5f521660045260245ffd5b50803b1561565456fe9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a2646970667358221220ca645654f5368c8af4b30261adb145fb22dbbb1d16a200861dc30351ff8afa8d64736f6c634300081c0033f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00
Deployed Bytecode
0x6080806040526004361015610012575f80fd5b5f3560e01c90816305d1e1dd14613763575080630d336011146110b6578063128dd84c1461368b57806313bebb82146135b35780631cfc4fb8146134795780631dbf12bf1461327557806324900d7c1461316757806326e3cc25146131245780632898d53014612d98578063374403d71461292a5780633bde56441461289f578063421f9a6e146128405780634542c92d146126a75780634934f507146122635780634b586fa01461210d5780634c47322e146120ca5780634d0175981461205b5780634da27ac6146120305780634f062c5a14611f4d5780634f1ef28614611d0f5780634f90b4a214611c8e5780634fe2e75c14611c1857806352d1902d14611b9f57806357e10ace14611b77578063715018a614611b04578063720abf891461189c5780638699201914611848578063883c9078146116eb5780638d94b36b146116255780638da5cb5b146115f35780638f0e15a714611473578063900ba78a1461139e5780639c2c3fe0146111f7578063a2b4044e146110bb578063a77da6ba146110b6578063acaace591461107b578063acfda69e1461103a578063ad3cb1cc14610fef578063ad3e6e1414610e4d578063b35218c014610dc2578063b4e4568c14610d91578063bf01c65014610d07578063bfecf6ce14610b91578063c208e2de14610b68578063c9fe814b14610a87578063cf73b2c81461087f578063cfe564c914610726578063d7f71ac31461052c578063d940ef70146104f5578063e0cc3eb6146103e9578063ea17532e14610336578063eb9f15be146102e0578063f2fde38b146102b55763ff2c58141461026e575f80fd5b346102b15760203660031901126102b1576004355f52600a60205260ff60405f205460881c165f52600c602052602061ffff60405f205460081c16604051908152f35b5f80fd5b346102b15760203660031901126102b1576102de6102d1613dc2565b6102d9614cb8565b61447a565b005b346102b15760203660031901126102b1576004356001600160401b0381116102b15760ff602061031581933690600401613d0a565b91908260405193849283378101600d81520301902054166040519015158152f35b346102b15760203660031901126102b1575f6040805161035581613e70565b82815282602082015201526004355f52600b60205260405f206040519061037b82613e70565b549064ffffffffff8216815260ff8260281c1691602082019060078410156103d5576103cc8260609564ffffffffff945283604086019360301c168352836040519551168552516020850190613f4f565b51166040820152f35b634e487b7160e01b5f52602160045260245ffd5b346102b15760203660031901126102b1576004355f52600a60205261ffff60405f20805490610498610429600361042260018501614332565b93016143f0565b9160405194846001600160401b038796168652818160401c166020870152818160501c16604087015264ffffffffff8160601c16606087015260ff8160881c16608087015260ff8160901c16151560a087015260981c1660c085015261012060e0850152610120840190613f5c565b828103610100840152602080835192838152019201905f5b8181106104be575050500390f35b825180516001600160a01b031685526020908101516001600160501b031681860152869550604090940193909201916001016104b0565b346102b15761050336613dd8565b905f52600a602052600260405f2001905f52602052602060ff60405f2054166040519015158152f35b346102b15761053a36613f80565b60015460405163e743862960e01b81523360048201526024810184905292939290602090829060449082906001600160a01b03165afa90811561071b575f916106ec575b50156106dd57815f52600a60205260405f205464ffffffffff8160601c16156106ce5760901c60ff166106bf576105b59082615486565b5f828152600b60205260409020805490929064ffffffffff166106b05764ffffffffff835460301c168061063c575b83546affffffffff0000000000001916603084901b69ffffffff0000000000001617845560408051848152602081018490527f5b4036fee23699edc6c33868b48eea7f82a415ae2715865db9ec6b74cc4701459190a1005b91908183146106a1577f5b4036fee23699edc6c33868b48eea7f82a415ae2715865db9ec6b74cc470145937f86ef48d3478dfc87f7a61b90ffdd3193d9862a68632a4c233e71d330b3bb5ce5604080958151908152846020820152a1935090916105e4565b63177366d160e01b5f5260045ffd5b6376cfa8c160e01b5f5260045ffd5b63142197d960e21b5f5260045ffd5b63d699f7e960e01b5f5260045ffd5b630a71f87f60e31b5f5260045ffd5b61070e915060203d602011610714575b6107068183613e8b565b810190613f9a565b8461057e565b503d6106fc565b6040513d5f823e3d90fd5b346102b15760603660031901126102b1576004356024356001600160401b0381116102b157610759903690600401613d0a565b60015460405163e743862960e01b815233600482015260448035602483018190529495939492602091839182906001600160a01b03165afa90811561071b575f91610860575b50156106dd57805f52600b6020528164ffffffffff60405f2054160361085157805f52600b60205260ff60405f205460281c1660078110156103d5576005116108425760c88311610833576108287f0ff410db68b95933b3297828599ba335d9c49f904355784c6a2bbbe839541dbc9460405194859485526060602086015260608501916143d0565b9060408301520390a1005b6347e8b47560e11b5f5260045ffd5b63d52ab5d160e01b5f5260045ffd5b6325d7e13960e11b5f5260045ffd5b610879915060203d602011610714576107068183613e8b565b8561079f565b346102b15761088d36613e1e565b60015460405163e743862960e01b8152336004820152602481018390529194929190602090829060449082906001600160a01b03165afa90811561071b575f91610a68575b50156106dd57835f52600b6020528164ffffffffff60405f2054160361085157835f52600b60205260ff60405f205460281c1660078110156103d55760021161084257815f52600a60205260405f205460ff8160881c165f52600c60205261ffff610946838260405f209460501c166140fe565b915460081c1610610a595763ffffffff82165f5b82811061099a5750507fac65b29e315dbc954a8da88cc0a5b5c6a59d3223452c29b42b5db6494a4f7d26936109959160405194859485614153565b0390a1005b6109a581848761410b565b35845f52600a6020526109f760405f20825f526002810160205260405f2060ff1981541690556109dc61ffff825460501c166153d0565b61ffff60501b82549160501b169061ffff60501b1916179055565b5f52600b60205260405f20908464ffffffffff835460301c1603610a4a5781546affffffffffff00000000001984166affffffffffffffffffffff1990911617650100000000001790915560010161095a565b63637df6ff60e11b5f5260045ffd5b632b229f6b60e11b5f5260045ffd5b610a81915060203d602011610714576107068183613e8b565b856108d2565b346102b157610a9536613dd8565b600354604051627eeac760e11b81523360048201526024810183905290602090829060449082906001600160a01b03165afa90811561071b575f91610b36575b5015610b2757805f52600b6020528164ffffffffff60405f2054160361085157815f52600a6020526001600160401b0360405f205416610b18576102de91615054565b6315d7cf2960e31b5f5260045ffd5b63078eec1360e11b5f5260045ffd5b90506020813d602011610b60575b81610b5160209383613e8b565b810103126102b1575183610ad5565b3d9150610b44565b346102b1575f3660031901126102b15760206001600160501b0360045460a01c16604051908152f35b346102b15760403660031901126102b1576004356001600160401b0381116102b157610bc1903690600401613dee565b600354604051627eeac760e11b815233600482015260248035908201819052939291602090829060449082906001600160a01b03165afa90811561071b575f91610cd5575b5015610b27578015610cc7575f5b818110610c6057507f284330b7aeacaa076942744a635969b490cf5cafa294d5b6c311f387078afa2892610c5560405193849360408552604085019161412f565b9060208301520390a1005b610c6b81838561410b565b3590815f52600a602052600260405f2001855f5260205260ff60405f20541615610cb8576001915f52600a602052600260405f2001855f5260205260405f2060ff19815416905501610c14565b6362bfd17560e11b5f5260045ffd5b6254c4fb60e71b5f5260045ffd5b90506020813d602011610cff575b81610cf060209383613e8b565b810103126102b1575184610c06565b3d9150610ce3565b346102b15760403660031901126102b1576004356001600160401b0381116102b157610d37903690600401613dee565b610d3f613d87565b610d47614cb8565b151560ff165f5b828110610d5757005b806001600160a01b03610d75610d70600194878961410b565b61419d565b165f52600f60205260405f208360ff1982541617905501610d4e565b346102b15760203660031901126102b1576004355f52600a602052602061ffff60405f205460981c16604051908152f35b346102b15760203660031901126102b1576004356001600160501b038116908181036102b1577f7934a8db3f8f75ea0c15b07050e7abbb51f58cfa3914f76450dc170f3d40d81191602091610e15614cb8565b6004805469ffffffffffffffffffff60a01b191660a09290921b69ffffffffffffffffffff60a01b16919091179055604051908152a1005b346102b15760603660031901126102b157602435600435610e6c613d46565b600354604051627eeac760e11b81523360048201526024810185905291929190602090829060449082906001600160a01b03165afa90811561071b575f91610fbd575b5015610b2757805f52600a60205260ff60405f205460881c165f52600c60205260405f205460ff81169081156106ce5760ff841691821115610fae57610ef4826144f8565b815f52600c6020526001600160501b038060405f205460681c169160681c169003936001600160501b038511610f9a57610f87606094610f5d6001600160501b037ff926d4ef55149e1fe1f7e953b8bddc590c485497a4f01d73ea589b8150bfed839816614b19565b5f858152600a60205260409020805460ff60881b191660889290921b60ff60881b16919091179055565b60405192835260208301526040820152a1005b634e487b7160e01b5f52601160045260245ffd5b63c59efceb60e01b5f5260045ffd5b90506020813d602011610fe7575b81610fd860209383613e8b565b810103126102b1575184610eaf565b3d9150610fcb565b346102b1575f3660031901126102b157611036604051611010604082613e8b565b60058152640352e302e360dc1b6020820152604051918291602083526020830190613f5c565b0390f35b346102b15760203660031901126102b1576001600160a01b0361105b613dc2565b611063614cb8565b166001600160601b0360a01b60135416176013555f80f35b346102b15760203660031901126102b15760206110ac6004355f52600b60205264ffffffffff60405f205416151590565b6040519015158152f35b613d56565b346102b15760603660031901126102b1576004356110d7613d87565b600354604051627eeac760e11b815233600482015260448035602483018190529492602091839182906001600160a01b03165afa90811561071b575f916111c5575b5015610b2757825f52600b6020528064ffffffffff60405f2054160361085157825f52600b60205260ff60405f205460281c1660078110156103d557600211610842575f818152600a60205260409020805460ff60901b1916831560901b60ff60901b161790557fdef22600eb997387bdb5a7edea2add06b5e92c0fda852986932731166349355192610995906040519384938460409194939260608201958252151560208201520152565b90506020813d6020116111ef575b816111e060209383613e8b565b810103126102b1575184611119565b3d91506111d3565b346102b15760803660031901126102b15760443560043560243560078310156102b157600354604051627eeac760e11b8152336004820152606435602482018190529391602090829060449082906001600160a01b03165afa90811561071b575f9161136c575b5015610b2757815f52600b6020528064ffffffffff60405f2054160361085157815f52600b60205260ff60405f205460281c1693835f52600b60205260ff60405f205460281c166007811015806103d5578282111561135e5784860361133f5760078710156103d5576103d5578511611330575b60078510156103d557808514611321578085111561131557806112fa57506102de9350614ceb565b919060066102de9503614fdb57611310816153e3565b614fdb565b91906102de9450614fdb565b635f5db10960e11b5f5260045ffd5b63bea1b71360e01b5f5260045ffd5b5060078610156103d55785106112d257631b1613f760e01b5f5260045ffd5b624da49d60e71b5f5260045ffd5b90506020813d602011611396575b8161138760209383613e8b565b810103126102b157518561125e565b3d915061137a565b346102b1576113ac36613dd8565b600354604051627eeac760e11b81523360048201526024810183905290602090829060449082906001600160a01b03165afa90811561071b575f91611441575b5015610b27577f86ef48d3478dfc87f7a61b90ffdd3193d9862a68632a4c233e71d330b3bb5ce591816040925f52600b602052825f206affffffffff00000000000019815416905582519182526020820152a1005b90506020813d60201161146b575b8161145c60209383613e8b565b810103126102b15751836113ec565b3d915061144f565b346102b15761148136613f80565b60015460405163e743862960e01b81523360048201526024810184905291939190602090829060449082906001600160a01b03165afa90811561071b575f916115d4575b50156106dd57805f52600a60205260405f20916002830193815f528460205260ff60405f20541615610cb85761150d825f52600b60205264ffffffffff60405f205416151590565b6106b05761151b9083615486565b825460ff8160881c165f52600c60205261ffff8060405f205460081c169160501c161015610a595761158d6040937f4029968a3f2d34f7c8df00f90946159de86b935a5c57f786888ce5d1d7845d9c95835f52602052845f2060ff1981541690556109dc61ffff825460501c166153d0565b5f818152600b60209081529084902080546affffffffff0000000000001965ffffffffffff1990911663ffffffff86161765010000000000171690558351928352820152a1005b6115ed915060203d602011610714576107068183613e8b565b846114c5565b346102b1575f3660031901126102b15760206001600160a01b035f5160206156825f395f51905f525416604051908152f35b346102b15761163336613efd565b9061163c614cb8565b5f5b828110611676576040517feda6ff97e5243629083aff63c9a0f9a5ad50c6d235c74b51e9ec92dc6ad5f6db9080610995868683614247565b60ff61168b611686838686614229565b614239565b161515806116c4575b156116b557806116af6116aa6001938686614229565b6151c1565b0161163e565b632520e1bd60e11b5f5260045ffd5b5060ff6116d5611686838686614229565b165f52600c60205260ff60405f20541615611694565b346102b1576116f936613e1e565b600354604051627eeac760e11b81523360048201526024810183905291929190602090829060449082906001600160a01b03165afa90811561071b575f91611816575b5015610b2757815f52600b6020528364ffffffffff60405f2054160361085157815f52600b60205260ff60405f205460281c1660078110156103d557600211610842578015611807575f5b8181106117c257507f5ceebf1e578529395e97c10d2075cccca96caf69041edf3d24f9a95bf42b696893916109959160405194859485614153565b6117cd81838661410b565b355f52600b60205260405f20908564ffffffffff835460301c1603610a4a5781546affffffffff0000000000001916909155600101611787565b63d411ab5760e01b5f5260045ffd5b90506020813d602011611840575b8161183160209383613e8b565b810103126102b157518561173c565b3d9150611824565b346102b15760203660031901126102b1576004355f52600b60205264ffffffffff60405f2054165f52600a602052611036611888600160405f2001614332565b604051918291602083526020830190613f5c565b346102b15760803660031901126102b1576004356024356001600160401b0381116102b1576118cf903690600401613dee565b916044356001600160401b0381116102b1576118ef903690600401613dee565b600354604051627eeac760e11b8152336004820152606435602482018190529396929591602090829060449082906001600160a01b03165afa90811561071b575f91611ad2575b5015610b27575f5b82811061194757005b61195281848461410b565b359061195f81888a61410b565b359160078310156102b157600354604051627eeac760e11b81523360048201526024810188905290602090829060449082906001600160a01b03165afa90811561071b575f91611aa1575b5015610b2757805f52600b6020528664ffffffffff60405f2054160361085157805f52600b60205260ff60405f205460281c16865f52600b60205260ff60405f205460281c166007811015806103d5578582111561135e57838903611a825760078310156103d5576103d5578111611330575b60078110156103d55783811461132157600193879181811115611a775781611a51575050611a4b9188614ceb565b0161193e565b926006611a649414611a69575b89614fdb565b611a4b565b611a728a6153e3565b611a5e565b50611a649289614fdb565b5060078210156103d5578110611a1d57631b1613f760e01b5f5260045ffd5b90506020813d8211611aca575b81611abb60209383613e8b565b810103126102b157518a6119aa565b3d9150611aae565b90506020813d602011611afc575b81611aed60209383613e8b565b810103126102b1575187611936565b3d9150611ae0565b346102b1575f3660031901126102b157611b1c614cb8565b5f6001600160a01b035f5160206156825f395f51905f52546001600160601b0360a01b81165f5160206156825f395f51905f5255167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346102b1576020611b90611b8a36613dd8565b906142ff565b611b9d6040518092613f4f565bf35b346102b1575f3660031901126102b1576001600160a01b037f000000000000000000000000dd015ce70a7cb53eae97cf6c8d4e90b8dbd6262d163003611c095760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b63703e46dd60e11b5f5260045ffd5b346102b15760203660031901126102b15760043561ffff8116908181036102b1577fbb4df7a4ea901e618bc10245eee20e4c8522555bfe10816b8128332bc0e334a991602091611c66614cb8565b6003805461ffff60c81b191660c89290921b61ffff60c81b16919091179055604051908152a1005b346102b157611c9c36613efd565b611ca4614cb8565b5f5b818110611cdf57507fab0ba102ddeb1d896e289b4c68719d8a1f4a198be3ab6883484a1fe929718a4f9161099560405192839283614247565b80611cfb60ff611cf56116866001958789614229565b166144f8565b611d096116aa828587614229565b01611ca6565b60403660031901126102b157611d23613dc2565b6024356001600160401b0381116102b157366023820112156102b157611d53903690602481600401359101613ec7565b6001600160a01b037f000000000000000000000000dd015ce70a7cb53eae97cf6c8d4e90b8dbd6262d16803014908115611f18575b50611c0957611d95614cb8565b6040516352d1902d60e01b81526001600160a01b0383169290602081600481875afa5f9181611ee4575b50611dd75783634c9c8ce360e01b5f5260045260245ffd5b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc859203611ed25750813b15611ec0577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b031916821790557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a2815115611ea8575f808360206102de95519101845af43d15611ea0573d91611e8483613eac565b92611e926040519485613e8b565b83523d5f602085013e615627565b606091615627565b505034611eb157005b63b398979f60e01b5f5260045ffd5b634c9c8ce360e01b5f5260045260245ffd5b632a87526960e21b5f5260045260245ffd5b9091506020813d602011611f10575b81611f0060209383613e8b565b810103126102b157519085611dbf565b3d9150611ef3565b90506001600160a01b037f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416141583611d88565b346102b15760203660031901126102b1575f60a0604051611f6d81613e55565b82815282602082015282604082015282606082015282608082015201526004355f52600c60205260c060405f206001600160501b0360405191611faf83613e55565b5464ffffffffff60ff82169384815262ffffff6020820161ffff8560081c16815261ffff60408401818760181c168152816060860193858960281c1685528960a060808901988a8c60401c168a52019960681c1689526040519a8b52511660208a0152511660408801525116606086015251166080840152511660a0820152f35b346102b157602061204036613dd8565b5f52600b825264ffffffffff60405f20541614604051908152f35b346102b15760403660031901126102b15760243561ffff811681036102b1576001600160a01b036008541633036120bb576004355f908152600a60205260409020805461ffff60981b191660989290921b61ffff60981b16919091179055005b63993eecd960e01b5f5260045ffd5b346102b15760203660031901126102b1576004355f52600b60205264ffffffffff60405f2054165f52600a602052602060ff60405f205460881c16604051908152f35b346102b15761211b36613e1e565b600354604051627eeac760e11b81523360048201526024810183905292949290602090829060449082906001600160a01b03165afa90811561071b575f91612231575b5015610b2757805f52600b6020528164ffffffffff60405f2054160361085157805f52600b60205260ff60405f205460281c1660078110156103d55760021161084257815f52600a60205260405f20938015610cc75760025f9501945b8181106121f5576040517f473f1c3e2a696b3007a52e476d439236942f08e0cff330fc24aeefab41d0d98e908061099586868a8a85614153565b61220081838761410b565b3590815f528660205260ff60405f20541615610cb8576001915f528660205260405f2060ff198154169055016121bb565b90506020813d60201161225b575b8161224c60209383613e8b565b810103126102b157518561215e565b3d915061223f565b346102b15760603660031901126102b1576024356004356001600160401b0382116102b157366023830112156102b1578160040135906001600160401b0382116102b1576024830192602436918460061b0101116102b15760015460405163e743862960e01b815233600482015260448035602483018190529392602091839182906001600160a01b03165afa90811561071b575f91612688575b50156106dd57815f52600b6020528064ffffffffff60405f2054160361085157815f52600b60205260ff60405f205460281c1660078110156103d55760051161084257600583116126795761235283614176565b926123606040519485613e8b565b80845261236c81614176565b602085019590601f19013687376001600160a01b03600654165f5b8381106124ec575050825f52600a602052600360405f2001906801000000000000000083116124d85781548383558084106124b2575b50905f5260205f205f915b8383106124465750505050604051926060840191845260606020850152518091526080830193905f5b818110612427577f72efcd8f70847517ad3bc1486f78c9e8353718305008d04c90e86e8eacea2bdc8580888760408301520390a1005b82516001600160a01b03168652602095860195909201916001016123f1565b60016040826001600160a01b0361245d849561419d565b86546001600160a01b031916911617855561247a602082016141b1565b855469ffffffffffffffffffff60a01b191660a09190911b69ffffffffffffffffffff60a01b161785559290940193920191016123c8565b825f528360205f2091820191015b8181106124cd57506123bd565b5f81556001016124c0565b634e487b7160e01b5f52604160045260245ffd5b6124fa610d7082868661418d565b906001600160a01b0360405192633af32abf60e01b84521691826004820152602081602481875afa90811561071b575f9161265b575b501561264c576001600160501b03612554602061254e84898961418d565b016141b1565b166102d181036125d857506040516301ffc9a760e01b81526380ac58cd60e01b6004820152602081602481865afa90811561071b575f916125ba575b50156125ab576001915b6125a4828a614202565b5201612387565b63adc1f5ed60e01b5f5260045ffd5b6125d2915060203d8111610714576107068183613e8b565b8a612590565b6104830361263d576040516301ffc9a760e01b8152636cdb3d1360e11b6004820152602081602481865afa90811561071b575f9161261f575b50156125ab5760019161259a565b612637915060203d8111610714576107068183613e8b565b8a612611565b630c1516e360e31b5f5260045ffd5b63f8f94f4960e01b5f5260045ffd5b612673915060203d8111610714576107068183613e8b565b8a612530565b636e58b71560e01b5f5260045ffd5b6126a1915060203d602011610714576107068183613e8b565b856122fe565b346102b1576126b536613e1e565b600354604051627eeac760e11b81523360048201526024810183905291929190602090829060449082906001600160a01b03165afa90811561071b575f9161280e575b5015610b2757815f52600b6020528364ffffffffff60405f2054160361085157815f52600b60205260ff60405f205460281c1660078110156103d55760021161084257835f52600a60205260405f205460ff8160881c165f52600c60205261ffff61276c838260405f209460501c166140fe565b915460081c1610610a59575f5b8181106127b457507f5b1a562619687202d271aec5b478cdde4e6e135bbdec7a5f7a11bf3056b5342693916109959160405194859485614153565b6127bf81838661410b565b3590855f52600a602052600260405f2001825f528060205260ff60405f2054166127ff576001925f5260205260405f208260ff1982541617905501612779565b63cbc57df560e01b5f5260045ffd5b90506020813d602011612838575b8161282960209383613e8b565b810103126102b15751856126f8565b3d915061281c565b346102b15761284e36613dd8565b90815f52600b60205264ffffffffff60405f2054161480612877575b6020906040519015158152f35b505f52600b60205260ff60405f205460281c1660078110156103d5576004602091101561286a565b346102b15760203660031901126102b157602460206001600160a01b036002541660405192838092631e7bbfab60e01b825260043560048301525afa801561071b576020915f916128fd575b506001600160a01b0360405191168152f35b61291d9150823d8411612923575b6129158183613e8b565b81019061400a565b826128eb565b503d61290b565b346102b1576101803660031901126102b157612944613dc2565b6064356001600160401b0381116102b157612963903690600401613d0a565b91906084356001600160401b0381116102b157612984903690600401613d0a565b60a4949194356001600160401b0381116102b1576129a6903690600401613d0a565b60c4969196356001600160401b0381116102b1576129c8903690600401613d0a565b939092610164359687151588036102b1576001600160a01b03601254163303612d89576044355f908152600a60205260409020805471ff000000000000000000ffffffffffffffff19166024356001600160401b0316176101243560881b60ff60881b1617815596875461ffff60401b191660e43560401b61ffff60401b16178855875466ffffffffffffff60501b19166101043560601b64ffffffffff60601b1617600160501b178855875461ffff60981b19166101443560981b61ffff60981b161788556024355f908152600b60205260409020805465ffffffffffff191660443563ffffffff1617650600000000001790556001600160401b0381116124d857612ae581612adc60018b015461404c565b60018b0161409a565b5f601f8211600114612ce25795899791955f9c7fbd0ab160e05d3247ed89c8619b640c22634a65d36949c146e3133fc319ccfb4d96612bac968f60209f9d9b97612c469f9b8d6001612b538380612b899e612b849891612cd7575b508160011b915f199060031b1c19161790565b9101555b612b72612b6d612b68368486613ec7565b6150f3565b6140df565b805460ff191660011790553691613ec7565b614a79565b60405191829160443583526024358b84015260c0604084015260c0830190613fb2565b60e43560608301526101243560808301526101043560a08301520390a1612c6e575b50507fd0f236da2a55211dd4cf5df9ab238a0858e8d127f01ce8d39d4299657e6d10c86040805160443581526101443586820152a16001600160a01b036002541690604051948580948193638471af7760e01b83526044359060048401602090939291936001600160a01b0360408201951681520152565b03925af1801561071b57612c5657005b6102de9060203d602011612923576129158183613e8b565b805460ff60901b191682151560901b60ff60901b161790557fdef22600eb997387bdb5a7edea2add06b5e92c0fda852986932731166349355190612ccd9060408051604435815291156020830152602435908201529081906060820190565b0390a18480612bce565b90508601355f612b40565b600189015f5260205f20905f5b601f1984168110612d715750955f9c7fbd0ab160e05d3247ed89c8619b640c22634a65d36949c146e3133fc319ccfb4d96612bac96612b8460209f9d9b9597612c469f9d97612b89998f9d82601f19811610612d58575b50508d60018083811b01910155612b57565b8301355f19600384901b60f8161c191690555f80612d46565b90916020600181928588013581550193019101612cef565b637fea9dc560e01b5f5260045ffd5b346102b1576101403660031901126102b1576004356001600160a01b0381168091036102b1576024356001600160a01b0381168091036102b157612dda613d96565b91612de3613dac565b926084356001600160501b038116938482036102b15760a435926001600160a01b0384168094036102b15760c4359361ffff8516928386036102b15760e4359564ffffffffff871687036102b15761010435976001600160a01b0389168099036102b15761012435996001600160a01b038b16809b036102b1575f5160206156a25f395f51905f52549060ff8260401c16159c8d6001600160401b0384168015918261311c575b506001149081613112575b159081613109575b506130fa577fbb4df7a4ea901e618bc10245eee20e4c8522555bfe10816b8128332bc0e334a99a60209a8f958c957f7934a8db3f8f75ea0c15b07050e7abbb51f58cfa3914f76450dc170f3d40d8119760016001600160401b03198316175f5160206156a25f395f51905f52556130ce575b50612f1861545b565b612f2061545b565b612f293361447a565b612f3161545b565b6001600160601b0360a01b5f5416175f556001600160a01b036003549a16916001600160a01b0360045494846001600160601b0360a01b871617600455166001600160601b0360a01b600554161760055564ffffffffff60a01b9060a01b1698898966ffffffffffffff60c81b8d1617176003556001600160601b0360a01b6006541617600655612fc0614cb8565b7fffff0000000000000000000000000000000000000000000000000000000000009092161760a09190911b69ffffffffffffffffffff60a01b1617600455604051908152a161300d614cb8565b7fffffffffff000000000000000000000000000000000000000000000000000000909316171760c89190911b61ffff60c81b1617600355604051908152a16001600160601b0360a01b60125416176012556001600160601b0360a01b601354161760135561307757005b68ff0000000000000000195f5160206156a25f395f51905f5254165f5160206156a25f395f51905f52557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a1005b68ffffffffffffffffff191668010000000000000001175f5160206156a25f395f51905f52555f612f0f565b63f92ee8a960e01b5f5260045ffd5b9050158f612e9d565b303b159150612e95565b91508f612e8a565b346102b15760203660031901126102b1576004355f52600a60205260ff60405f205460881c165f52600c602052602061ffff60405f205460181c16604051908152f35b346102b15760a03660031901126102b1576004356001600160a01b0381168091036102b1576024356001600160a01b0381168091036102b1576131a8613d96565b916131b1613dac565b90608435936001600160a01b0385168095036102b1576131cf614cb8565b600254936001600160a01b03851681811591821561326b575b50501561325c576001600160a01b039485936001600160601b0360a01b60015416176001556001600160601b0360a01b1617600255166001600160601b0360a01b6007541617600755166001600160601b0360a01b60085416176008556001600160601b0360a01b60095416176009555f80f35b6337b8bea960e21b5f5260045ffd5b14905081886131e8565b346102b15760e03660031901126102b1576004356024356001600160401b0381116102b1576132a8903690600401613d0a565b90916044356001600160401b0381116102b1576132c9903690600401613d0a565b9290936064356001600160401b0381116102b1576132eb903690600401613d0a565b6084939193356001600160401b0381116102b15761330d903690600401613d0a565b60015460405163e743862960e01b815233600482015260c435602482018190529960a4359893959392909190602090829060449082906001600160a01b03165afa90811561071b575f9161345a575b50156106dd57895f52600b6020528864ffffffffff60405f2054160361085157895f52600b60205260ff60405f205460281c1660078110156103d557600511610842577fff9c4aa2c485437fd53cb8c1462dd050bd76f4e240718699dd1bb1bb248c6be49a61340261341597613433998c5f52600a60205260ff60405f205460881c165f52600c6020526133fc62ffffff60405f205460281c168d61451d565b8c61454b565b61343e575b612b848787878787876148a1565b60405194859485526020850152608060408501526080840190613fb2565b9060608301520390a1005b6134556001600160501b0360045460a01c16614b19565b613407565b613473915060203d602011610714576107068183613e8b565b8c61335c565b346102b15760603660031901126102b15760443560243560043560078310156102b1575f818152600a6020908152604091829020546003549251627eeac760e11b81523360048201526001600160401b03909116602482015291829060449082906001600160a01b03165afa90811561071b575f91613581575b5015610b2757815f52600b6020528064ffffffffff60405f2054160361085157805f52600a6020526001600160401b0360405f20541692838314613572578015613563576006811015613554578361354f916102de9584614fdb565b615054565b63cbcd7cd360e01b5f5260045ffd5b5061354f836102de9483614ceb565b633493d67f60e11b5f5260045ffd5b90506020813d6020116135ab575b8161359c60209383613e8b565b810103126102b15751846134f3565b3d915061358f565b346102b15760603660031901126102b1576004356024359064ffffffffff82168092036102b157604435908115158092036102b157335f52600f60205260ff60405f2054161561367c57805f52600a60205260405f20918254938064ffffffffff8660a81c16019364ffffffffff8511610f9a577ff34537c7169eedc3a5bf2996afbe8ee8754382962b6a4276ee7d450e49748a0f9560609564ffffffffff60a81b9060a81b169064ffffffffff60a81b191617905560405192835260208301526040820152a1005b63e5e9973760e01b5f5260045ffd5b346102b15760603660031901126102b15760043560ff8116908181036102b1576024359160ff8316928381036102b1576136c3613d46565b6136cb614cb8565b606460ff6136e2836136dd8689614038565b614038565b1603613754577fb4ae64ee51c0d84410a16847b3f8f3d7ac7bbbeaf2abae8671beba816f0279e39460609460ff936005548560b01b8560b01b16928660a01b9060a01b169062ffffff60a01b191617908560a81b9060a81b1617176005556040519384526020840152166040820152a1005b63196f075b60e11b5f5260045ffd5b346102b15760e03660031901126102b157600435906024356001600160401b0381116102b157613797903690600401613d0a565b916044356001600160401b0381116102b1576137b7903690600401613d0a565b6064949194356001600160401b0381116102b1576137d9903690600401613d0a565b6084929192356001600160401b0381116102b1576137fb903690600401613d0a565b93909260a4359861ffff8a1696878b036102b15760c4359960ff8b16998a8c036102b15760015463e743862960e01b8252336004830152602482018f9052602090829060449082906001600160a01b03165afa90811561071b575f91613ceb575b50156106dd5761387e8d5f52600b60205264ffffffffff60405f205416151590565b6106b057895f52600c60205260405f209b6138988b6144f8565b8c5460281c62ffffff166138ac908b61451d565b6003548060a01c64ffffffffff169c64ffffffffff8e14610f9a578f90613999908f64ffffffffff95613952918760a01b6001820160a01b168860a01b198816176003555f52600a60205261393660405f20936001600160401b03808816166001600160401b0319865416178555849081549060ff60881b9060881b169060ff60881b1916179055565b825461ffff60401b191660409190911b61ffff60401b16178255565b805466ffffffffffffff60501b191642861660601b64ffffffffff60601b1617600160501b178155600354815461ffff60981b191660309190911c61ffff60981b16179055565b5f52600b6020528163ffffffff60405f209260a01c161682198254161781556506000000000065ff0000000000198254161781555460301c168d81613bba575b505095613a5495613a729a956001600160501b039a95613a33955f9f9b958f907fbd0ab160e05d3247ed89c8619b640c22634a65d36949c146e3133fc319ccfb4d9c613a249261454b565b50612b848787878787876148a1565b916040519384938a85528c602086015260c0604086015260c0850190613fb2565b91606084015260808301524260a08301520390a15460681c16614b19565b601354600254604051638471af7760e01b8152336004820152602481019390935291926001600160a01b039182169260209285926044928492165af191821561071b575f92613b99575b5060206001600160a01b03600154169360246040518096819363231b667d60e21b835260048301525afa92831561071b575f93613b67575b5060846020925f6001600160a01b0393604051968795869463ec01f01960e01b86526013600487015216602485015215156044840152600160648401525af1801561071b57613b3f57005b6102de9060203d602011613b60575b613b588183613e8b565b810190614029565b503d613b4e565b6001600160a01b039193506020925f613b8e608493863d8811610714576107068183613e8b565b959350509250613af4565b613bb391925060203d602011612923576129158183613e8b565b9083613abc565b600354604051627eeac760e11b81523360048201526024810192909252969d959b949a9599939892979496919491602090829060449082906001600160a01b03165afa90811561071b575f91613cb9575b5015610b27575f9c613a729b8f9b8f8f9b613a549b6001600160501b039f9d7f86ef48d3478dfc87f7a61b90ffdd3193d9862a68632a4c233e71d330b3bb5ce57fbd0ab160e05d3247ed89c8619b640c22634a65d36949c146e3133fc319ccfb4d9f613a339c81604092613a249852600b6020525f8390206affffffffff00000000000019815416905582519182526020820152a150969c50969c9e50509550959a50959a9c50958d6139d9565b90506020813d602011613ce3575b81613cd460209383613e8b565b810103126102b157518f613c0b565b3d9150613cc7565b613d04915060203d602011610714576107068183613e8b565b8e61385c565b9181601f840112156102b1578235916001600160401b0383116102b157602083818601950101116102b157565b359061ffff821682036102b157565b6044359060ff821682036102b157565b346102b15760203660031901126102b1576004355f52600b602052602064ffffffffff60405f205416604051908152f35b6024359081151582036102b157565b604435906001600160a01b03821682036102b157565b606435906001600160a01b03821682036102b157565b600435906001600160a01b03821682036102b157565b60409060031901126102b1576004359060243590565b9181601f840112156102b1578235916001600160401b0383116102b1576020808501948460051b0101116102b157565b9060606003198301126102b15760043591602435906001600160401b0382116102b157613e4d91600401613dee565b909160443590565b60c081019081106001600160401b038211176124d857604052565b606081019081106001600160401b038211176124d857604052565b90601f801991011681019081106001600160401b038211176124d857604052565b6001600160401b0381116124d857601f01601f191660200190565b929192613ed382613eac565b91613ee16040519384613e8b565b8294818452818301116102b1578281602093845f960137010152565b9060206003198301126102b1576004356001600160401b0381116102b157826023820112156102b1578060040135926001600160401b0384116102b157602460c08502830101116102b1576024019190565b9060078210156103d55752565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b60609060031901126102b157600435906024359060443590565b908160209103126102b1575180151581036102b15790565b9080602083519182815201916020808360051b8301019401925f915b838310613fdd57505050505090565b9091929394602080613ffb600193601f198682030187528951613f5c565b97019301930191939290613fce565b908160209103126102b157516001600160a01b03811681036102b15790565b908160209103126102b1575190565b9060ff8091169116019060ff8211610f9a57565b90600182811c9216801561407a575b602083101461406657565b634e487b7160e01b5f52602260045260245ffd5b91607f169161405b565b81811061408f575050565b5f8155600101614084565b9190601f81116140a957505050565b6140d3925f5260205f20906020601f840160051c830193106140d5575b601f0160051c0190614084565b565b90915081906140c6565b60208091604051928184925191829101835e8101600d81520301902090565b91908201809211610f9a57565b919081101561411b5760051b0190565b634e487b7160e01b5f52603260045260245ffd5b81835290916001600160fb1b0383116102b15760209260051b809284830137010190565b9493929160409261417192875260606020880152606087019161412f565b930152565b6001600160401b0381116124d85760051b60200190565b919081101561411b5760061b0190565b356001600160a01b03811681036102b15790565b356001600160501b03811681036102b15790565b80511561411b5760200190565b80516001101561411b5760400190565b80516002101561411b5760600190565b80516003101561411b5760800190565b805182101561411b5760209160051b010190565b81810292918115918404141715610f9a57565b919081101561411b5760c0020190565b3560ff811681036102b15790565b60208082528101839052604001915f905b8082106142655750505090565b909192833560ff81168091036102b157815261ffff61428660208601613d37565b16602082015261ffff61429b60408601613d37565b166040820152606084013562ffffff81168091036102b1576060820152608084013564ffffffffff81168091036102b157608082015260a0840135906001600160501b0382168092036102b15760c08160019360a083940152019401920190614258565b815f52600b60205264ffffffffff60405f2054161461431d57505f90565b5f52600b60205260ff60405f205460281c1690565b9060405191825f8254926143458461404c565b80845293600181169081156143ae575060011461436a575b506140d392500383613e8b565b90505f9291925260205f20905f915b8183106143925750509060206140d3928201015f61435d565b6020919350806001915483858901015201910190918492614379565b9050602092506140d394915060ff191682840152151560051b8201015f61435d565b908060209392818452848401375f828201840152601f01601f1916010190565b908154916143fd83614176565b9261440b6040519485613e8b565b80845260208401915f5260205f20915f905b82821061442a5750505050565b604051604081018181106001600160401b038211176124d85760019283926020926040526001600160501b0388546001600160a01b038116835260a01c168382015281520194019101909261441d565b6001600160a01b031680156144e5576001600160a01b035f5160206156825f395f51905f5254826001600160601b0360a01b8216175f5160206156825f395f51905f5255167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3565b631e4fbdf760e01b5f525f60045260245ffd5b5f52600c60205260ff60405f2054161561450e57565b630bec67bf60e31b5f5260045ffd5b908115159182614540575b50501561453157565b6305615c0b60e11b5f5260045ffd5b111590505f80614528565b9192905f61458c9173e3223eaf0e260b54a8ce777ac9f4a972310370c095604051938492839263b2dad15560e01b84526020600485015260248401916143d0565b0381875af490811561071b575f91614828575b508093600382511061481957601482511161480a57602060405180926391ae2cdb60e01b825282600483015281806145da6024820188613f5c565b03915af490811561071b575f916147eb575b50156147dc576145fb816150f3565b925f52600a602052600160405f200190614617612b6883614332565b9360405194602086018151966146476020828186019a808c875e81015f838201520301601f198101835282613e8b565b51902091604051926020840182519461467a60208281870198808a875e81015f838201520301601f198101835282613e8b565b5190201415968761468e575b505050505050565b60ff60405160208185518089835e8101600d81520301902054166147cd576020925190816147a9575b5050604051928391518091835e8101600d815203019020600160ff198254161790558051906001600160401b0382116124d8576146fe826146f8855461404c565b8561409a565b602090601f83116001146147465761472d92915f918361473b575b50508160011b915f199060031b1c19161790565b90555b5f8080808080614686565b015190505f80614719565b90601f19831691845f52815f20925f5b8181106147915750908460019594939210614779575b505050811b019055614730565b01515f1960f88460031b161c191690555f808061476c565b92936020600181928786015181550195019301614756565b839082604051938492835e8101600d81520301902060ff1981541690555f806146b7565b633480a50360e21b5f5260045ffd5b634caa0cad60e11b5f5260045ffd5b614804915060203d602011610714576107068183613e8b565b5f6145ec565b63680b6caf60e01b5f5260045ffd5b6365ac5e6760e11b5f5260045ffd5b90503d805f833e6148398183613e8b565b8101906020818303126102b1578051906001600160401b0382116102b1570181601f820112156102b15780519061486f82613eac565b9261487d6040519485613e8b565b828452602083830101116102b157815f9260208093018386015e830101525f61459f565b949392909460198611614a6a5785158015614a5f575b15614a505760206148fa9173e3223eaf0e260b54a8ce777ac9f4a972310370c09760405193849283926309f241ed60e41b845285600485015260248401916143d0565b0381895af490811561071b575f91614a31575b5015614a225760198111614a13576149449160209160405193849283926309f241ed60e41b845285600485015260248401916143d0565b0381875af490811561071b575f916149f4575b50156149e657601982116149d75761498f926020926040518095819482936309f241ed60e41b845287600485015260248401916143d0565b03915af490811561071b575f916149b8575b50156149a957565b6388d04d1f60e01b5f5260045ffd5b6149d1915060203d602011610714576107068183613e8b565b5f6149a1565b6348997f7160e01b5f5260045ffd5b620f7f1b60e91b5f5260045ffd5b614a0d915060203d602011610714576107068183613e8b565b5f614957565b6304121ae360e31b5f5260045ffd5b63088cc68560e21b5f5260045ffd5b614a4a915060203d602011610714576107068183613e8b565b5f61490d565b638c72c2a160e01b5f5260045ffd5b5060048610156148b7565b630da1592560e21b5f5260045ffd5b9695929194939060405195614a8f60a088613e8b565b6004875260805f5b818110614b0857505092614af69492614ad0614ae393899896614b059a9c614abe8b6141c5565b52614ac88a6141c5565b503691613ec7565b614ad9886141d2565b52614ac8876141d2565b614aec856141e2565b52614ac8846141e2565b614aff826141f2565b526141f2565b50565b806060602080938c01015201614a97565b5f906001600160a01b035f54166064614b3a60ff60055460a01c1684614216565b04813b156102b15760405163079cc67960e41b815233600482015260248101919091525f8160448183865af1801561071b57614ca3575b50614bcc60206001600160a01b03600454166064614b9760ff60055460a81c1687614216565b6040516323b872dd60e01b81523360048201526001600160a01b03909316602484015204604482015291829081906064820190565b038187865af18015614c985791849391602093614c469650614c7d575b506001600160a01b03846064614c096005549560ff8760b01c1690614216565b0493604051978895869485936323b872dd60e01b85521633600485016001600160a01b036040929594938160608401971683521660208201520152565b03925af1908115614c715750614c595750565b614b059060203d602011610714576107068183613e8b565b604051903d90823e3d90fd5b614c9390843d8611610714576107068183613e8b565b614be9565b6040513d86823e3d90fd5b614cb09193505f90613e8b565b5f915f614b71565b6001600160a01b035f5160206156825f395f51905f5254163303614cd857565b63118cdaa760e01b5f523360045260245ffd5b905f92825f52600a60205260405f20826001600160401b0382541614614fcd575b61ffff815460501c16908115610f9a5780546bffff0000000000000000000019165f1990920160501b61ffff60501b1691909117815561ffff905460501c16614f915750815f52600a60205260405f2061ffff815460501c16614f8257612b6d612b686001614d7b9301614332565b60ff198154169055815f52600a602052600360405f205f815560018101614da2815461404c565b9081614f3f575b5050018054905f815581614f21575b50507fe5cdc676962c829e36cbb7b0c60a322b60e2d01db199590aab156803c5a97ae66020604051848152a15b5f818152600b60205260409020805465ffffffffffff191690556007546001600160a01b0316803b156102b1575f8091604460405180948193637792156760e11b83528860048401528760248401525af1801561071b57614f0c575b506001600160a01b0360085416803b15614eef57838091604460405180948193637792156760e11b83528860048401528760248401525af18015614c9857908491614ef3575b50506001600160a01b036009541691823b15614eef579060448492836040519586948593637792156760e11b8552600485015260248401525af18015614ee457614ecf575050565b614eda828092613e8b565b614ee15750565b80fd5b6040513d84823e3d90fd5b8380fd5b81614efd91613e8b565b614f0857825f614e87565b8280fd5b614f199193505f90613e8b565b5f915f614e41565b5f5260205f20908101905b81811015614db8575f8155600101614f2c565b81601f5f9311600114614f565750555b5f80614da9565b81835260208320614f7291601f0160051c810190600101614084565b8082528160208120915555614f4f565b6370141ec960e11b5f5260045ffd5b60607fa21e607ac5445b7603f2e7e913bb2a66f680c289fb1a2ce735eb6c92713c5dd991604051908582528460208301526040820152a1614de5565b614fd6846153e3565b614d0c565b909192825f52600b60205260405f2060078510156103d5577f57ff30d4395dd2630a292b8a50feba238650ef9136dd5961908b899bb1ba9916946080948261504c935465ff00000000008460281b169065ff0000000000191617905560405194855260208501526040840190613f4f565b6060820152a1565b5f818152600a60209081526040808320805467ffffffffffffffff19166001600160401b038716179055600e8252808320805464ffffffffff19169055848352600b825291829020805465ff000000000019166506000000000017905581519283528201929092527fb36e6603e4ebbca03e98d06ae8a134eb0c59baf95557ee9403a9fe073b6ac0749190a1565b90815181101561411b570160200190565b90615121602080936040519481869251918291018484015e81015f838201520301601f198101845283613e8b565b5f5b8251811015614b055761513681846150e2565b51604160f81b6001600160f81b03198216908110159081615193575b50615161575b50600101615123565b60f81c6020019060ff8211610f9a5760019160f81b6001600160f81b0319165f1a61518c82866150e2565b5390615158565b602d60f91b101590505f615152565b3561ffff811681036102b15790565b3562ffffff811681036102b15790565b60ff6151cc82614239565b16600181116152af575b5f52600c60205260405f209060ff6151ed82614239565b1682549162ffff00615201602083016151a2565b60081b169264ffff000000615218604084016151a2565b60181b169067ffffff0000000000615232606085016151b1565b60281b169160808401359464ffffffffff861686036102b1576cffffffffff00000000000000009469ffffffffffffffffffff60681b906152759060a0016141b1565b60681b16966001600160501b0360681b199386199267ffffff0000000000199164ffffffffff1916171617161716179160401b1617179055565b5f198101818111610f9a575f52600c60205260405f20604051906152d282613e55565b549060ff821681526020810161ffff8360081c16815260a0604083019261ffff8560181c1684526001600160501b03606082019562ffffff8160281c16875264ffffffffff8160401c16608084015260681c1691829101526001600160501b0361533e60a088016141b1565b16106153c15761ffff80615354602088016151a2565b9251169116106153b25761ffff8061536e604087016151a2565b9251169116106153a35762ffffff80615389606086016151b1565b925116911610156151d657633f07a64560e01b5f5260045ffd5b6383202d3d60e01b5f5260045ffd5b637dc1f31b60e01b5f5260045ffd5b636dddf41160e11b5f5260045ffd5b61ffff1661ffff8114610f9a5760010190565b5f818152600a60209081526040808320805467ffffffffffffffff198116909155600e835292819020805464ffffffffff19164264ffffffffff1617905580519384526001600160401b0392909216908301527f81ec2aca54fed63234990c222e20c265fbd80545574659c8e359fe9dc3cda16291a1565b60ff5f5160206156a25f395f51905f525460401c161561547757565b631afcd79f60e31b5f5260045ffd5b5f52600a60205261549c600360405f20016143f0565b80516154a6575050565b5f92835b825185108061561f575b15615604576104836001600160501b0360206154d08887614202565b510151160361556a57506001600160a01b036154ec8584614202565b5151604051627eeac760e11b8152336004820152602481018690529160209183916044918391165afa90811561071b575f91615539575b501515935b5f198114610f9a57600101936154aa565b90506020813d8211615562575b8161555360209383613e8b565b810103126102b157515f615523565b3d9150615546565b936102d16001600160501b0360206155828487614202565b510151160361552857602460206001600160a01b036155a18487614202565b515116604051928380926331a9108f60e11b82528960048301525afa5f91816155e4575b506155d1575b50615528565b6001600160a01b0316331494505f6155cb565b6155fd91925060203d8111612923576129158183613e8b565b905f6155c5565b92509250501561561057565b63466e5a3d60e11b5f5260045ffd5b5080156154b4565b9061564b575080511561563c57805190602001fd5b63d6bda27560e01b5f5260045ffd5b81511580615678575b61565c575090565b6001600160a01b0390639996b31560e01b5f521660045260245ffd5b50803b1561565456fe9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a2646970667358221220ca645654f5368c8af4b30261adb145fb22dbbb1d16a200861dc30351ff8afa8d64736f6c634300081c0033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in S
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.