Source Code
Overview
ETH Balance
ETH 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:
ProfileManagerUpgradeable
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import "./mocks/MockTrailblazerProfile.sol"; import "./interfaces/IAddressManager.sol"; import "./interfaces/IExperienceManager.sol"; import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "./libraries/errors/TrailblazerErrors.sol"; /** * @title ProfileManagerUpgradeable * @author taiko.xyz * @notice Upgradeable manager for Trailblazer profile NFTs with comprehensive feature system * @dev Manages soulbound profile NFTs with equipable RMRK functionality and feature flags * * Key Features: * - Soulbound profile NFT management (1 per address) * - Feature flag system with admin-only flags for competitions * - Equipment and asset management via RMRK * - Experience and level tracking * - Game statistics tracking * - Competition flag management * - Integration with AddressManager for decoupled architecture * * Security: * - Profile creation requires verification * - Admin-only flags cannot be self-set * - Role-based permissions for sensitive operations * - Upgradeable pattern for future enhancements * * @custom:security-contact [email protected] */ contract ProfileManagerUpgradeable is Initializable, AccessControlUpgradeable, ReentrancyGuardUpgradeable, UUPSUpgradeable { // ============================================ // Libraries & Structs // ============================================ struct ProfileData { uint256 gamesPlayed; uint256 wins; uint256 createdAt; bool exists; mapping(string => bool) featureFlags; mapping(string => string) stringData; mapping(string => uint256) numericData; mapping(address => uint256) gameStats; // game contract => games played mapping(address => uint256) gameWins; // game contract => wins } // ============================================ // Constants & Roles // ============================================ /// @notice Role for administrators bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); /// @notice Role for game managers (can update stats) bytes32 public constant GAME_MANAGER_ROLE = keccak256("GAME_MANAGER_ROLE"); /// @notice Role for upgrade authority bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE"); /// @notice Role for competition managers bytes32 public constant COMPETITION_MANAGER_ROLE = keccak256("COMPETITION_MANAGER_ROLE"); /// @notice Role for flag managers (backend services that can set flags) bytes32 public constant FLAG_MANAGER_ROLE = keccak256("FLAG_MANAGER_ROLE"); // ============================================ // State Variables // ============================================ /// @notice The profile NFT contract MockTrailblazerProfile public nftContract; /// @notice Address manager for contract discovery IAddressManager public addressManager; /// @notice Mapping of token ID to profile data mapping(uint256 => ProfileData) private _profiles; /// @notice Mapping to track authorized game managers mapping(address => bool) public authorizedGameManagers; /// @notice Mapping to track profile creation requirements mapping(address => bool) public canCreateProfile; /// @notice Whether profile creation is open to all bool public openProfileCreation; /// @notice Minimum level required for certain flags mapping(string => uint256) public flagLevelRequirements; /// @notice Trusted signer address for signature verification address public trustedSigner; /// @notice Mapping to track used nonces to prevent replay attacks mapping(bytes32 => bool) public usedNonces; /// @notice Array of flags that are required for all games string[] private _requiredFlags; /// @notice Mapping to quickly check if a flag is required mapping(string => bool) private _isRequiredFlag; // ============================================ // Events // ============================================ event ProfileCreated(address indexed owner, uint256 indexed tokenId); event ProfileUpdated(address indexed owner, uint256 indexed tokenId); event ProfileBurned(address indexed owner, uint256 indexed tokenId); event FeatureFlagSet(address indexed owner, uint256 indexed tokenId, string flag, bool value); event GameStatsUpdated(address indexed player, address indexed game, bool isWin); event CompetitionFlagGranted(address indexed player, string flag, address indexed granter); event TrustedSignerUpdated(address indexed oldSigner, address indexed newSigner); event DiscordBound(address indexed player, string discordId, bytes32 indexed nonce); event PlayerFlagSetWithSignature(address indexed player, string flag, bool value, bytes32 indexed nonce); event RequiredFlagAdded(string indexed flag, address indexed addedBy); event RequiredFlagRemoved(string indexed flag, address indexed removedBy); // ============================================ // Modifiers // ============================================ modifier onlyWithProfile(address player) { if (!nftContract.hasNFT(player)) revert TrailblazerErrors.NoProfile(); _; } modifier canManageGameStats() { if (!(hasRole(GAME_MANAGER_ROLE, msg.sender) || authorizedGameManagers[msg.sender])) { revert TrailblazerErrors.Unauthorized(); } _; } // ============================================ // Initializer // ============================================ /** * @notice Initializes the ProfileManagerUpgradeable * @param _nftContract Address of the TrailblazerProfileNFT contract * @param _addressManager Address of the AddressManager contract * @param _openCreation Whether profile creation is open to all */ function initialize(address _nftContract, address _addressManager, bool _openCreation) public initializer { if (_nftContract == address(0)) revert TrailblazerErrors.ZeroAddress(); if (_addressManager == address(0)) revert TrailblazerErrors.ZeroAddress(); __AccessControl_init(); __ReentrancyGuard_init(); __UUPSUpgradeable_init(); nftContract = MockTrailblazerProfile(_nftContract); addressManager = IAddressManager(_addressManager); openProfileCreation = _openCreation; // Setup roles _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); _grantRole(ADMIN_ROLE, msg.sender); _grantRole(UPGRADER_ROLE, msg.sender); _grantRole(GAME_MANAGER_ROLE, msg.sender); _grantRole(COMPETITION_MANAGER_ROLE, msg.sender); // Setup default flag level requirements _setupDefaultFlagRequirements(); } /** * @notice Sets up default level requirements for certain flags */ function _setupDefaultFlagRequirements() private { flagLevelRequirements["VETERAN_PLAYER"] = 10; flagLevelRequirements["EXPERT_PLAYER"] = 25; flagLevelRequirements["MASTER_PLAYER"] = 50; flagLevelRequirements["GUILD_LEADER"] = 20; } // ============================================ // Upgrade Authorization // ============================================ function _authorizeUpgrade(address newImplementation) internal override onlyRole(UPGRADER_ROLE) {} // ============================================ // Profile Management // ============================================ /** * @notice Creates a profile for the sender */ function createProfile() external nonReentrant { if (!(openProfileCreation || canCreateProfile[msg.sender] || hasRole(ADMIN_ROLE, msg.sender))) { revert TrailblazerErrors.Unauthorized(); } if (nftContract.hasNFT(msg.sender)) revert TrailblazerErrors.AlreadyInitialized(); // Mint profile NFT uint256 tokenId = nftContract.mintProfile(msg.sender); // Initialize profile data ProfileData storage profile = _profiles[tokenId]; profile.gamesPlayed = 0; profile.wins = 0; profile.createdAt = block.timestamp; profile.exists = true; // Set default flags profile.featureFlags["isActive"] = true; profile.featureFlags["canPlayGames"] = true; emit ProfileCreated(msg.sender, tokenId); } /** * @notice Admin creates a profile for a specific address * @param player The address to create profile for */ function adminCreateProfile(address player) external onlyRole(ADMIN_ROLE) nonReentrant { if (player == address(0)) revert TrailblazerErrors.ZeroAddress(); if (nftContract.hasNFT(player)) revert TrailblazerErrors.AlreadyInitialized(); uint256 tokenId = nftContract.mintProfile(player); ProfileData storage profile = _profiles[tokenId]; profile.gamesPlayed = 0; profile.wins = 0; profile.createdAt = block.timestamp; profile.exists = true; profile.featureFlags["isActive"] = true; profile.featureFlags["canPlayGames"] = true; emit ProfileCreated(player, tokenId); } /** * @notice Burns a profile (admin only or self) * @param player The address to burn profile from */ function burnProfile(address player) external nonReentrant { if (!hasRole(ADMIN_ROLE, msg.sender)) revert TrailblazerErrors.Unauthorized(); if (!nftContract.hasNFT(player)) revert TrailblazerErrors.NoProfile(); uint256 tokenId = nftContract.getTokenId(player); // Clear profile data (mappings will be cleared automatically) delete _profiles[tokenId]; // Burn NFT nftContract.burnFrom(player); emit ProfileBurned(player, tokenId); } // ============================================ // Feature Flag Management // ============================================ /** * @notice Sets a feature flag for a player (FLAG_MANAGER_ROLE only) * @param player The player address * @param flag The flag name * @param value The flag value */ function setFeatureFlag(address player, string memory flag, bool value) external onlyRole(FLAG_MANAGER_ROLE) onlyWithProfile(player) { uint256 tokenId = nftContract.getTokenId(player); // Check level requirements if (flagLevelRequirements[flag] > 0) { (, uint256 playerLevel) = _getExperienceData(player, 0, 1); if (playerLevel < flagLevelRequirements[flag]) revert TrailblazerErrors.PlayerNotEligible(); } _profiles[tokenId].featureFlags[flag] = value; emit FeatureFlagSet(player, tokenId, flag, value); } /** * @notice Admin sets any feature flag * @param player The player address * @param flag The flag name * @param value The flag value */ function adminSetFeatureFlag(address player, string memory flag, bool value) external onlyRole(ADMIN_ROLE) onlyWithProfile(player) { uint256 tokenId = nftContract.getTokenId(player); _profiles[tokenId].featureFlags[flag] = value; emit FeatureFlagSet(player, tokenId, flag, value); } /** * @notice Grants competition-specific flags * @param players Array of player addresses * @param flag The competition flag to grant */ function grantCompetitionFlag(address[] calldata players, string memory flag) external onlyRole(COMPETITION_MANAGER_ROLE) { for (uint256 i = 0; i < players.length; i++) { if (nftContract.hasNFT(players[i])) { uint256 tokenId = nftContract.getTokenId(players[i]); _profiles[tokenId].featureFlags[flag] = true; emit CompetitionFlagGranted(players[i], flag, msg.sender); } } } /** * @notice Sets competition flag for a single player (called by TournamentManager) * @param player The player address * @param flag The competition flag to set * @param value The flag value (true/false) */ function setCompetitionFlag(address player, string memory flag, bool value) external onlyRole(COMPETITION_MANAGER_ROLE) onlyWithProfile(player) { uint256 tokenId = nftContract.getTokenId(player); _profiles[tokenId].featureFlags[flag] = value; emit FeatureFlagSet(player, tokenId, flag, value); } /** * @notice Gets feature flag value for a player * @param player The player address * @param flag The flag name * @return value The flag value */ function getFeatureFlag(address player, string memory flag) external view onlyWithProfile(player) returns (bool value) { uint256 tokenId = nftContract.getTokenId(player); return _profiles[tokenId].featureFlags[flag]; } // ============================================ // Game Statistics // ============================================ /** * @notice Updates game statistics for a player * @param player The player address * @param game The game contract address * @param isWin Whether the player won */ function updateGameStats(address player, address game, bool isWin) external canManageGameStats onlyWithProfile(player) { uint256 tokenId = nftContract.getTokenId(player); ProfileData storage profile = _profiles[tokenId]; profile.gamesPlayed++; profile.gameStats[game]++; if (isWin) { profile.wins++; profile.gameWins[game]++; } emit GameStatsUpdated(player, game, isWin); } // ============================================ // View Functions // ============================================ /** * @notice Checks if an address has a profile */ function hasProfile(address player) external view returns (bool) { return nftContract.hasNFT(player); } /** * @notice Gets comprehensive profile information */ function getProfile(address player) external view returns ( uint256 experience, uint256 level, uint256 gamesPlayed, uint256 wins, uint256 createdAt, bool exists, bool isActive, bool canPlay ) { if (!nftContract.hasNFT(player)) { return (0, 0, 0, 0, 0, false, false, false); } uint256 tokenId = nftContract.getTokenId(player); ProfileData storage profile = _profiles[tokenId]; // Get experience from ExperienceManager or fall back to default values (uint256 actualExp, uint256 actualLvl) = _getExperienceData(player, 0, 1); return ( actualExp, actualLvl, profile.gamesPlayed, profile.wins, profile.createdAt, profile.exists, profile.featureFlags["isActive"], profile.featureFlags["canPlayGames"] ); } /** * @notice Internal helper to get experience data from ExperienceManager * @param player The player address * @param fallbackExp Fallback experience value * @param fallbackLevel Fallback level value * @return experience The actual experience * @return level The actual level */ function _getExperienceData(address player, uint256 fallbackExp, uint256 fallbackLevel) internal view returns (uint256 experience, uint256 level) { address experienceManager = addressManager.getAddress("EXPERIENCE_MANAGER"); if (experienceManager != address(0)) { try IExperienceManager(experienceManager).getPlayerExperience(player) returns (uint256 exp) { experience = exp; // Get the correctly calculated level from ExperienceManager level = IExperienceManager(experienceManager).getPlayerLevel(player); return (experience, level); } catch { // Fall back to stored values if ExperienceManager call fails } } return (fallbackExp, fallbackLevel); } /** * @notice Gets game-specific statistics */ function getGameStats(address player, address game) external view returns (uint256 gamesPlayed, uint256 wins) { if (!nftContract.hasNFT(player)) return (0, 0); uint256 tokenId = nftContract.getTokenId(player); ProfileData storage profile = _profiles[tokenId]; return (profile.gameStats[game], profile.gameWins[game]); } /** * @notice Checks if player can play games (has profile, enabled, and meets required flags) * @param player The player address to check * @return canPlay True if player can participate in games * @dev This function now also validates that the player has all required flags */ function canPlayGames(address player) external view returns (bool canPlay) { if (!nftContract.hasNFT(player)) return false; uint256 tokenId = nftContract.getTokenId(player); ProfileData storage profile = _profiles[tokenId]; // Check basic eligibility first if (!(profile.exists && profile.featureFlags["isActive"] && profile.featureFlags["canPlayGames"])) { return false; } // Check required flags return this.hasAllRequiredFlags(player); } // ============================================ // Administrative Functions // ============================================ /** * @notice Sets profile creation authorization */ function setCanCreateProfile(address user, bool canCreate) external onlyRole(ADMIN_ROLE) { canCreateProfile[user] = canCreate; } /** * @notice Toggles open profile creation */ function setOpenProfileCreation(bool open) external onlyRole(ADMIN_ROLE) { openProfileCreation = open; } /** * @notice Authorizes a game manager */ function setAuthorizedGameManager(address manager, bool authorized) external onlyRole(ADMIN_ROLE) { authorizedGameManagers[manager] = authorized; } /** * @notice Sets level requirement for a flag */ function setFlagLevelRequirement(string memory flag, uint256 level) external onlyRole(ADMIN_ROLE) { flagLevelRequirements[flag] = level; } /** * @notice Updates the address manager */ function updateAddressManager(address newAddressManager) external onlyRole(ADMIN_ROLE) { if (newAddressManager == address(0)) revert TrailblazerErrors.ZeroAddress(); addressManager = IAddressManager(newAddressManager); } // ============================================ // Signature Verification System // ============================================ /** * @notice Verifies a signature using ecrecover * @param player The player address * @param data The data being signed (e.g., discordId or flag) * @param nonce The nonce to prevent replay attacks * @param expiry The expiry timestamp * @param signature The signature to verify * @return isValid True if signature is valid and not expired */ function _verifySignature( address player, string memory data, bytes32 nonce, uint256 expiry, bytes memory signature ) internal view returns (bool isValid) { if (trustedSigner == address(0)) revert TrailblazerErrors.TrustedSignerNotSet(); if (block.timestamp > expiry) revert TrailblazerErrors.SignatureExpired(); if (usedNonces[nonce]) revert TrailblazerErrors.NonceAlreadyUsed(); bytes32 messageHash = keccak256(abi.encodePacked(player, data, nonce, expiry)); bytes32 ethSignedMessageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash)); address recoveredSigner = _recoverSigner(ethSignedMessageHash, signature); return recoveredSigner == trustedSigner; } /** * @notice Recovers the signer address from a signature * @param hash The hash that was signed * @param signature The signature * @return signer The recovered signer address */ function _recoverSigner(bytes32 hash, bytes memory signature) internal pure returns (address signer) { if (signature.length != 65) revert TrailblazerErrors.InvalidSignature(); bytes32 r; bytes32 s; uint8 v; assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } if (v < 27) { v += 27; } if (v != 27 && v != 28) revert TrailblazerErrors.InvalidSignature(); signer = ecrecover(hash, v, r, s); if (signer == address(0)) revert TrailblazerErrors.InvalidSignature(); } /** * @notice Binds a Discord ID to a player's profile using signature verification * @param discordId The Discord ID to bind * @param nonce A unique nonce to prevent replay attacks * @param expiry The expiry timestamp for the signature * @param signature The signature from the trusted signer */ function bindDiscordWithSignature( string memory discordId, bytes32 nonce, uint256 expiry, bytes memory signature ) external onlyWithProfile(msg.sender) nonReentrant { if (bytes(discordId).length == 0) revert TrailblazerErrors.InvalidConfiguration(); if (!_verifySignature(msg.sender, discordId, nonce, expiry, signature)) { revert TrailblazerErrors.InvalidSignature(); } // Mark nonce as used usedNonces[nonce] = true; // Store Discord ID in profile uint256 tokenId = nftContract.getTokenId(msg.sender); _profiles[tokenId].stringData["discordId"] = discordId; emit DiscordBound(msg.sender, discordId, nonce); } /** * @notice Sets a player flag using signature verification * @param player The player address * @param flag The flag name to set * @param value The flag value (true/false) * @param nonce A unique nonce to prevent replay attacks * @param expiry The expiry timestamp for the signature * @param signature The signature from the trusted signer */ function setPlayerFlagWithSignature( address player, string memory flag, bool value, bytes32 nonce, uint256 expiry, bytes memory signature ) external onlyWithProfile(player) nonReentrant { if (bytes(flag).length == 0) revert TrailblazerErrors.InvalidConfiguration(); string memory flagValue = value ? "true" : "false"; if (!_verifySignature(player, string(abi.encodePacked(flag, ":", flagValue)), nonce, expiry, signature)) { revert TrailblazerErrors.InvalidSignature(); } // Mark nonce as used usedNonces[nonce] = true; // Set the flag uint256 tokenId = nftContract.getTokenId(player); _profiles[tokenId].featureFlags[flag] = value; emit PlayerFlagSetWithSignature(player, flag, value, nonce); emit FeatureFlagSet(player, tokenId, flag, value); } /** * @notice Sets the trusted signer address (admin only) * @param newTrustedSigner The new trusted signer address */ function setTrustedSigner(address newTrustedSigner) external onlyRole(ADMIN_ROLE) { if (newTrustedSigner == address(0)) revert TrailblazerErrors.ZeroAddress(); if (newTrustedSigner == trustedSigner) revert TrailblazerErrors.SameTrustedSigner(); address oldSigner = trustedSigner; trustedSigner = newTrustedSigner; emit TrustedSignerUpdated(oldSigner, newTrustedSigner); } /** * @notice Checks if a nonce has been used * @param nonce The nonce to check * @return used True if the nonce has been used */ function isNonceUsed(bytes32 nonce) external view returns (bool used) { return usedNonces[nonce]; } // ============================================ // String & Numeric Data Management // ============================================ /** * @notice Sets string data (player only) */ function setStringData(string memory key, string memory value) external onlyWithProfile(msg.sender) { uint256 tokenId = nftContract.getTokenId(msg.sender); _profiles[tokenId].stringData[key] = value; } /** * @notice Gets string data */ function getStringData(address player, string memory key) external view returns (string memory) { if (!nftContract.hasNFT(player)) return ""; uint256 tokenId = nftContract.getTokenId(player); return _profiles[tokenId].stringData[key]; } /** * @notice Sets numeric data (player only) */ function setNumericData(string memory key, uint256 value) external onlyWithProfile(msg.sender) { uint256 tokenId = nftContract.getTokenId(msg.sender); _profiles[tokenId].numericData[key] = value; } /** * @notice Gets numeric data */ function getNumericData(address player, string memory key) external view returns (uint256) { if (!nftContract.hasNFT(player)) return 0; uint256 tokenId = nftContract.getTokenId(player); return _profiles[tokenId].numericData[key]; } // ============================================ // Required Flag Management // ============================================ /** * @notice Adds a flag to the required flags list for all games * @param flag The flag name to require for all games * @dev Only admins can add required flags */ function addRequiredFlag(string memory flag) external onlyRole(ADMIN_ROLE) { if (bytes(flag).length == 0) revert TrailblazerErrors.InvalidConfiguration(); if (_isRequiredFlag[flag]) revert TrailblazerErrors.FlagAlreadySet(); _requiredFlags.push(flag); _isRequiredFlag[flag] = true; emit RequiredFlagAdded(flag, msg.sender); } /** * @notice Removes a flag from the required flags list * @param flag The flag name to remove from required list * @dev Only admins can remove required flags */ function removeRequiredFlag(string memory flag) external onlyRole(ADMIN_ROLE) { if (bytes(flag).length == 0) revert TrailblazerErrors.InvalidConfiguration(); if (!_isRequiredFlag[flag]) revert TrailblazerErrors.FlagNotFound(); // Find and remove the flag from the array for (uint256 i = 0; i < _requiredFlags.length; i++) { if (keccak256(abi.encodePacked(_requiredFlags[i])) == keccak256(abi.encodePacked(flag))) { // Move the last element to this position and remove the last element _requiredFlags[i] = _requiredFlags[_requiredFlags.length - 1]; _requiredFlags.pop(); break; } } _isRequiredFlag[flag] = false; emit RequiredFlagRemoved(flag, msg.sender); } /** * @notice Checks if a player has all required flags enabled * @param player The player address to check * @return hasAllRequired True if player has all required flags enabled * @dev This function is designed to be called by games to validate player eligibility */ function hasAllRequiredFlags(address player) external view returns (bool hasAllRequired) { if (!nftContract.hasNFT(player)) return false; uint256 tokenId = nftContract.getTokenId(player); ProfileData storage profile = _profiles[tokenId]; // If no required flags are set, player meets requirements if (_requiredFlags.length == 0) return true; // Check each required flag for (uint256 i = 0; i < _requiredFlags.length; i++) { if (!profile.featureFlags[_requiredFlags[i]]) { return false; } } return true; } /** * @notice Gets the list of all required flags * @return flags Array of flag names that are required for all games */ function getRequiredFlags() external view returns (string[] memory flags) { return _requiredFlags; } /** * @notice Checks if a specific flag is in the required flags list * @param flag The flag name to check * @return isRequired True if the flag is required for all games */ function isRequiredFlag(string memory flag) external view returns (bool isRequired) { return _isRequiredFlag[flag]; } /** * @notice Gets the missing required flags for a player * @param player The player address to check * @return missingFlags Array of flag names that the player is missing * @dev Useful for UIs to show which flags a player needs to enable */ function getMissingRequiredFlags(address player) external view returns (string[] memory missingFlags) { if (!nftContract.hasNFT(player)) { return _requiredFlags; // Player has no profile, so missing all required flags } uint256 tokenId = nftContract.getTokenId(player); ProfileData storage profile = _profiles[tokenId]; // Count missing flags first uint256 missingCount = 0; for (uint256 i = 0; i < _requiredFlags.length; i++) { if (!profile.featureFlags[_requiredFlags[i]]) { missingCount++; } } // Create array of missing flags missingFlags = new string[](missingCount); uint256 currentIndex = 0; for (uint256 i = 0; i < _requiredFlags.length; i++) { if (!profile.featureFlags[_requiredFlags[i]]) { missingFlags[currentIndex] = _requiredFlags[i]; currentIndex++; } } return missingFlags; } }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/access/AccessControl.sol";
/**
* @title MockTrailblazerProfile
* @notice Mock contract for testing ProfileManagerUpgradeable signature verification
*/
contract MockTrailblazerProfile is AccessControl {
mapping(address => bool) private _hasNFT;
mapping(address => uint256) private _tokenIds;
uint256 private _nextTokenId = 1;
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(MINTER_ROLE, msg.sender);
}
function hasNFT(address owner) external view returns (bool) {
return _hasNFT[owner];
}
function getTokenId(address owner) external view returns (uint256) {
require(_hasNFT[owner], "No NFT for this address");
return _tokenIds[owner];
}
function mintProfile(address to) external onlyRole(MINTER_ROLE) returns (uint256) {
require(!_hasNFT[to], "Already has NFT");
uint256 tokenId = _nextTokenId++;
_hasNFT[to] = true;
_tokenIds[to] = tokenId;
return tokenId;
}
function burnFrom(address from) external onlyRole(MINTER_ROLE) {
require(_hasNFT[from], "No NFT to burn");
_hasNFT[from] = false;
delete _tokenIds[from];
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/**
* @title IAddressManager
* @author taiko.xyz
* @notice Interface for AddressManager contract discovery
*/
interface IAddressManager {
function getAddress(string memory contractName) external view returns (address);
function tryGetAddress(string memory contractName) external view returns (bool found, address addr);
function setAddress(string memory contractName, address contractAddress) external;
function hasAddress(string memory contractName) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/**
* @title IExperienceManager
* @author taiko.xyz
* @notice Interface for the Experience Management system
* @dev Handles XP rewards, penalties, and game configurations
*/
interface IExperienceManager {
// ============================================
// Data Structures
// ============================================
/**
* @notice XP configuration for a specific game
* @param winReward XP reward for winning
* @param lossPenalty XP penalty for losing
* @param streakBonus Bonus XP for win streaks
* @param enabled Whether XP processing is enabled for this game
*/
struct GameXPConfig {
uint256 winReward;
uint256 lossPenalty;
uint256 streakBonus;
bool enabled;
}
// ============================================
// Events
// ============================================
/// @notice Emitted when a game's XP configuration is updated
event GameConfigured(
address indexed gameContract, uint256 winReward, uint256 lossPenalty, uint256 streakBonus, bool enabled
);
/// @notice Emitted when XP is processed for a player
event ExperienceProcessed(
address indexed player,
address indexed gameContract,
bool isWin,
uint256 xpChange,
uint256 newTotal,
uint256 newLevel
);
/// @notice Emitted when GameHub address is updated
event GameHubUpdated(address indexed newGameHub);
/// @notice Emitted when bonus XP is awarded
event BonusXPAwarded(address indexed player, uint256 xpAmount, string reason, uint256 newTotal, uint256 newLevel);
// ============================================
// Core Functions
// ============================================
/**
* @notice Processes game outcome for experience points
* @dev Only callable by SessionManager
* @param player The player address
* @param gameContract The game contract address
* @param isWin Whether the player won
* @param competitionId Competition ID for tournament-specific processing (bytes32(0) for regular games)
* @param stakeValue USD stake value with 18 decimals (0 for no stake)
*/
function processGameOutcome(
address player,
address gameContract,
bool isWin,
bytes32 competitionId,
uint256 stakeValue
) external;
/**
* @notice Awards tournament XP directly (called by TournamentManager)
* @param player The player to award XP to
* @param xpAmount The amount of XP to award
* @param competitionId The competition ID for tracking
*/
function awardCompetitionXP(address player, uint256 xpAmount, bytes32 competitionId) external;
/**
* @notice Awards bonus XP directly for achievements and special rewards
* @param player The player to award XP to
* @param xpAmount The amount of XP to award
* @param reason A description of why the bonus XP was awarded
*/
function awardBonusXP(address player, uint256 xpAmount, string calldata reason) external;
/**
* @notice Configures XP settings for a game
* @param gameContract The game contract address
* @param winReward XP reward for winning
* @param lossPenalty XP penalty for losing
* @param streakBonus Bonus XP for win streaks
* @param enabled Whether to enable XP for this game
*/
function configureGame(
address gameContract,
uint256 winReward,
uint256 lossPenalty,
uint256 streakBonus,
bool enabled
) external;
// ============================================
// View Functions
// ============================================
/**
* @notice Gets a player's total experience points
* @param player The player address
* @return The total XP
*/
function getPlayerExperience(address player) external view returns (uint256);
/**
* @notice Gets a player's calculated level based on experience
* @param player The player address
* @return The calculated level
*/
function getPlayerLevel(address player) external view returns (uint256);
/**
* @notice Gets XP configuration for a game
* @param gameContract The game contract address
* @return The GameXPConfig struct
*/
function getGameConfig(address gameContract) external view returns (GameXPConfig memory);
/**
* @notice Gets a player's win streak for a specific game
* @param player The player address
* @param gameContract The game contract address
* @return The current win streak
*/
function getPlayerStreak(address player, address gameContract) external view returns (uint256);
// ============================================
// Admin Functions
// ============================================
/**
* @notice Sets the GameHub address
* @param newGameHub The new GameHub contract address
*/
function setGameHub(address newGameHub) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
struct AccessControlStorage {
mapping(bytes32 role => RoleData) _roles;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;
function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
assembly {
$.slot := AccessControlStorageLocation
}
}
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
function __AccessControl_init() internal onlyInitializing {
}
function __AccessControl_init_unchained() internal onlyInitializing {
}
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
return $._roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
AccessControlStorage storage $ = _getAccessControlStorage();
return $._roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
AccessControlStorage storage $ = _getAccessControlStorage();
bytes32 previousAdminRole = getRoleAdmin(role);
$._roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
if (!hasRole(role, account)) {
$._roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` from `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
if (hasRole(role, account)) {
$._roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
* consider using {ReentrancyGuardTransient} instead.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuardUpgradeable is Initializable {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
/// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard
struct ReentrancyGuardStorage {
uint256 _status;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {
assembly {
$.slot := ReentrancyGuardStorageLocation
}
}
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
function __ReentrancyGuard_init() internal onlyInitializing {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal onlyInitializing {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
$._status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
// On the first call to nonReentrant, _status will be NOT_ENTERED
if ($._status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
$._status = ENTERED;
}
function _nonReentrantAfter() private {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
$._status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
return $._status == ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.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 reinitialization) 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 Pointer to storage slot. Allows integrators to override it with a custom storage location.
*
* NOTE: Consider following the ERC-7201 formula to derive storage locations.
*/
function _initializableStorageSlot() internal pure virtual returns (bytes32) {
return INITIALIZABLE_STORAGE;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
bytes32 slot = _initializableStorageSlot();
assembly {
$.slot := slot
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.22;
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.
*/
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
pragma solidity ^0.8.24;
/**
* @title TrailblazerErrors
* @author taiko.xyz
* @notice Standardized error messages for the Trailblazer gaming platform
* @dev Custom errors provide better debugging information
*/
library TrailblazerErrors {
// ============================================
// Access Control Errors
// ============================================
/// @dev Thrown when caller lacks required permissions
error Unauthorized();
/// @dev Thrown when caller is not the contract owner
error NotOwner();
/// @dev Thrown when caller is not a valid game hub
error NotGameHub();
/// @dev Thrown when caller is not the router
error NotRouter();
// ============================================
// Address Validation Errors
// ============================================
/// @dev Thrown when a required address is zero
error ZeroAddress();
/// @dev Thrown when an invalid game contract address is provided
error InvalidGame();
/// @dev Thrown when an invalid player address is provided
error InvalidPlayer();
// ============================================
// Session Management Errors
// ============================================
/// @dev Thrown when session ID is invalid or doesn't exist
error InvalidSession();
/// @dev Thrown when a session has already ended
error SessionEnded();
/// @dev Thrown when a session has expired
error SessionExpired();
/// @dev Thrown when a player is already in another game session
error PlayerAlreadyInGame();
/// @dev Thrown when trying to start a session with no players
error NoPlayersProvided();
/// @dev Thrown when too many players are provided for a session
error TooManyPlayers();
/// @dev Thrown when duplicate players are provided
error DuplicatePlayer();
/// @dev Thrown when TTL is invalid (too short or too long)
error InvalidTTL();
// ============================================
// Authentication Errors
// ============================================
/// @dev Thrown when authentication fails
error AuthenticationFailed();
/// @dev Thrown when a signature is invalid
error InvalidSignature();
/// @dev Thrown when a signature has already been used (replay attack)
error SignatureAlreadyUsed();
/// @dev Thrown when signature is too old (timestamp validation)
error SignatureExpired();
/// @dev Thrown when nonce is invalid (not sequential)
error InvalidNonce();
// ============================================
// Balance and Fee Errors
// ============================================
/// @dev Thrown when an amount is zero when it shouldn't be
error ZeroAmount();
/// @dev Thrown when there's insufficient balance for an operation
error InsufficientBalance();
/// @dev Thrown when trying to unlock more than is locked
error InsufficientLockedBalance();
/// @dev Thrown when a fee transfer fails
error FeeTransferFailed();
/// @dev Thrown when basis points don't sum to 10000
error InvalidBasisPoints();
// ============================================
// Contract State Errors
// ============================================
/// @dev Thrown when system is paused
error SystemPaused();
/// @dev Thrown when a specific game is paused
error GamePaused();
/// @dev Thrown when a contract is not properly initialized
error NotInitialized();
/// @dev Thrown when trying to initialize an already initialized contract
error AlreadyInitialized();
// ============================================
// Game-Specific Errors
// ============================================
/// @dev Thrown when a game is not whitelisted
error GameNotWhitelisted();
/// @dev Thrown when a game doesn't meet player count requirements
error InvalidPlayerCount();
/// @dev Thrown when game validation fails
error GameValidationFailed(string reason);
/// @dev Thrown when a player doesn't meet game requirements
error PlayerNotEligible();
// ============================================
// Batch Operation Errors
// ============================================
/// @dev Thrown when batch size is too large
error BatchSizeTooLarge();
/// @dev Thrown when batch operation fails
error BatchOperationFailed();
// ============================================
// Lock Mechanism Errors
// ============================================
/// @dev Thrown when trying to interact with an expired lock
error LockExpired();
/// @dev Thrown when trying to unlock when no lock exists
error NoActiveLock();
/// @dev Thrown when emergency unlock is not enabled
error EmergencyUnlockDisabled();
// ============================================
// Experience Processing Errors
// ============================================
/// @dev Thrown when experience manager is not configured
error ExperienceManagerNotConfigured();
/// @dev Thrown when experience processing fails for a player
error ExperienceProcessingFailed(address player, address game);
/// @dev Thrown when tournament experience processing fails
error TournamentExperienceProcessingFailed(bytes32 tournamentId, address player);
/// @dev Thrown when tournament manager is not configured
error TournamentManagerNotConfigured();
/// @dev Thrown when trying to award experience with invalid parameters
error InvalidExperienceParameters();
/// @dev Thrown when player is not registered for tournament
error PlayerNotRegisteredForTournament(bytes32 tournamentId, address player);
/// @dev Thrown when tournament is not active
error TournamentNotActive(bytes32 tournamentId);
// ============================================
// Configuration Errors
// ============================================
/// @dev Thrown when configuration is invalid
error InvalidConfiguration();
/// @dev Thrown when trying to set invalid fee configuration
error InvalidFeeConfiguration();
/// @dev Thrown when game mode is invalid
error InvalidGameMode();
// ============================================
// Stake-Related Errors
// ============================================
/// @dev Thrown when stake value is invalid (e.g., zero when required)
error InvalidStakeValue();
/// @dev Thrown when stake proof is invalid or verification fails
error InvalidStakeProof();
/// @dev Thrown when wrong number of players for staked games
error InvalidPlayers();
// ============================================
// Session Validation Errors
// ============================================
/// @dev Thrown when game is not active
error GameNotActive();
/// @dev Thrown when array lengths don't match in batch operations
error ArrayLengthMismatch();
/// @dev Thrown when a profile doesn't exist
error NoProfile();
/// @dev Thrown when player profile is inactive
error PlayerInactive();
/// @dev Thrown when player cannot play games
error PlayerCannotPlayGames();
/// @dev Thrown when auth module is not found
error AuthModuleNotFound();
/// @dev Thrown when profile manager is not found
error ProfileManagerNotFound();
/// @dev Thrown when session manager is not found
error SessionManagerNotFound();
/// @dev Thrown when not game owner
error NotGameOwner();
/// @dev Thrown when system is emergency paused
error EmergencyPaused();
/// @dev Thrown when caller is not the router
error OnlyRouter();
/// @dev Thrown when invalid address manager
error InvalidAddressManager();
/// @dev Thrown when invalid router address
error InvalidRouter();
// ============================================
// Experience Manager Errors
// ============================================
/// @dev Thrown when season duration is invalid
error InvalidSeasonDuration();
/// @dev Thrown when competition already exists
error CompetitionAlreadyExists();
/// @dev Thrown when multiplier is too high
error MultiplierTooHigh();
/// @dev Thrown when competition has ended
error CompetitionEnded();
/// @dev Thrown when player is already participating in competition
error AlreadyParticipating();
/// @dev Thrown when XP entry fee is insufficient
error InsufficientXPForEntry();
/// @dev Thrown when XP amount is invalid
error InvalidXPAmount();
/// @dev Thrown when reason is empty
error EmptyReason();
/// @dev Thrown when competition multiplier is too high
error CompetitionMultiplierTooHigh();
/// @dev Thrown when multiplier is invalid
error InvalidMultiplier();
/// @dev Thrown when duration is invalid
error InvalidDuration();
/// @dev Thrown when there are too many tiers
error TooManyTiers();
/// @dev Thrown when stakes are not in ascending order
error StakesNotAscending();
/// @dev Thrown when multipliers are not non-decreasing
error MultipliersNotNonDecreasing();
// ============================================
// Game Flag Management Errors
// ============================================
/// @dev Thrown when a flag already exists
error FlagAlreadySet();
/// @dev Thrown when a flag is not found
error FlagNotFound();
// ============================================
// Signature Verification Errors
// ============================================
/// @dev Thrown when a nonce has already been used
error NonceAlreadyUsed();
/// @dev Thrown when trusted signer is not set
error TrustedSignerNotSet();
/// @dev Thrown when setting the same trusted signer
error SameTrustedSigner();
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {IERC165, ERC165} from "../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
mapping(bytes32 role => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
return _roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
if (!hasRole(role, account)) {
_roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` from `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
if (hasRole(role, account)) {
_roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (access/IAccessControl.sol)
pragma solidity >=0.8.4;
/**
* @dev External interface of AccessControl declared to support ERC-165 detection.
*/
interface IAccessControl {
/**
* @dev The `account` is missing a role.
*/
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @dev The caller of a function is not the expected one.
*
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
*/
error AccessControlBadConfirmation();
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted to signal this.
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
* Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*/
function renounceRole(bytes32 role, address callerConfirmation) external;
}// 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.4.0) (utils/introspection/IERC165.sol)
pragma solidity >=0.4.16;
/**
* @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.4.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165Upgradeable is Initializable, IERC165 {
function __ERC165_init() internal onlyInitializing {
}
function __ERC165_init_unchained() internal onlyInitializing {
}
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/draft-IERC1822.sol)
pragma solidity >=0.4.16;
/**
* @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.4.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.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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 Context {
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.4.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (proxy/beacon/IBeacon.sol)
pragma solidity >=0.4.16;
/**
* @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.4.0) (interfaces/IERC1967.sol)
pragma solidity >=0.4.11;
/**
* @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.4.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, bytes memory returndata) = recipient.call{value: amount}("");
if (!success) {
_revert(returndata);
}
}
/**
* @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") {
revert(add(returndata, 0x20), mload(returndata))
}
} else {
revert Errors.FailedCall();
}
}
}// 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.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);
}{
"remappings": [
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"evm/=lib/evm/contracts/",
"forge-std/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/src/",
"halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[],"name":"AlreadyInitialized","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":"FlagAlreadySet","type":"error"},{"inputs":[],"name":"FlagNotFound","type":"error"},{"inputs":[],"name":"InvalidConfiguration","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"NoProfile","type":"error"},{"inputs":[],"name":"NonceAlreadyUsed","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"PlayerNotEligible","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"SameTrustedSigner","type":"error"},{"inputs":[],"name":"SignatureExpired","type":"error"},{"inputs":[],"name":"TrustedSignerNotSet","type":"error"},{"inputs":[],"name":"UUPSUnauthorizedCallContext","type":"error"},{"inputs":[{"internalType":"bytes32","name":"slot","type":"bytes32"}],"name":"UUPSUnsupportedProxiableUUID","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"player","type":"address"},{"indexed":false,"internalType":"string","name":"flag","type":"string"},{"indexed":true,"internalType":"address","name":"granter","type":"address"}],"name":"CompetitionFlagGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"player","type":"address"},{"indexed":false,"internalType":"string","name":"discordId","type":"string"},{"indexed":true,"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"DiscordBound","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"string","name":"flag","type":"string"},{"indexed":false,"internalType":"bool","name":"value","type":"bool"}],"name":"FeatureFlagSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"player","type":"address"},{"indexed":true,"internalType":"address","name":"game","type":"address"},{"indexed":false,"internalType":"bool","name":"isWin","type":"bool"}],"name":"GameStatsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"player","type":"address"},{"indexed":false,"internalType":"string","name":"flag","type":"string"},{"indexed":false,"internalType":"bool","name":"value","type":"bool"},{"indexed":true,"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"PlayerFlagSetWithSignature","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ProfileBurned","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ProfileCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ProfileUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"string","name":"flag","type":"string"},{"indexed":true,"internalType":"address","name":"addedBy","type":"address"}],"name":"RequiredFlagAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"string","name":"flag","type":"string"},{"indexed":true,"internalType":"address","name":"removedBy","type":"address"}],"name":"RequiredFlagRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldSigner","type":"address"},{"indexed":true,"internalType":"address","name":"newSigner","type":"address"}],"name":"TrustedSignerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"COMPETITION_MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FLAG_MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GAME_MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UPGRADER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UPGRADE_INTERFACE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"flag","type":"string"}],"name":"addRequiredFlag","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"addressManager","outputs":[{"internalType":"contract IAddressManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"}],"name":"adminCreateProfile","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"},{"internalType":"string","name":"flag","type":"string"},{"internalType":"bool","name":"value","type":"bool"}],"name":"adminSetFeatureFlag","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"authorizedGameManagers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"discordId","type":"string"},{"internalType":"bytes32","name":"nonce","type":"bytes32"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"bindDiscordWithSignature","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"}],"name":"burnProfile","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"canCreateProfile","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"}],"name":"canPlayGames","outputs":[{"internalType":"bool","name":"canPlay","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"createProfile","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"flagLevelRequirements","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"},{"internalType":"string","name":"flag","type":"string"}],"name":"getFeatureFlag","outputs":[{"internalType":"bool","name":"value","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"},{"internalType":"address","name":"game","type":"address"}],"name":"getGameStats","outputs":[{"internalType":"uint256","name":"gamesPlayed","type":"uint256"},{"internalType":"uint256","name":"wins","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"}],"name":"getMissingRequiredFlags","outputs":[{"internalType":"string[]","name":"missingFlags","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"},{"internalType":"string","name":"key","type":"string"}],"name":"getNumericData","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"}],"name":"getProfile","outputs":[{"internalType":"uint256","name":"experience","type":"uint256"},{"internalType":"uint256","name":"level","type":"uint256"},{"internalType":"uint256","name":"gamesPlayed","type":"uint256"},{"internalType":"uint256","name":"wins","type":"uint256"},{"internalType":"uint256","name":"createdAt","type":"uint256"},{"internalType":"bool","name":"exists","type":"bool"},{"internalType":"bool","name":"isActive","type":"bool"},{"internalType":"bool","name":"canPlay","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRequiredFlags","outputs":[{"internalType":"string[]","name":"flags","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"},{"internalType":"string","name":"key","type":"string"}],"name":"getStringData","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"players","type":"address[]"},{"internalType":"string","name":"flag","type":"string"}],"name":"grantCompetitionFlag","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"}],"name":"hasAllRequiredFlags","outputs":[{"internalType":"bool","name":"hasAllRequired","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"}],"name":"hasProfile","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_nftContract","type":"address"},{"internalType":"address","name":"_addressManager","type":"address"},{"internalType":"bool","name":"_openCreation","type":"bool"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"isNonceUsed","outputs":[{"internalType":"bool","name":"used","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"flag","type":"string"}],"name":"isRequiredFlag","outputs":[{"internalType":"bool","name":"isRequired","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nftContract","outputs":[{"internalType":"contract MockTrailblazerProfile","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"openProfileCreation","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"flag","type":"string"}],"name":"removeRequiredFlag","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"manager","type":"address"},{"internalType":"bool","name":"authorized","type":"bool"}],"name":"setAuthorizedGameManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bool","name":"canCreate","type":"bool"}],"name":"setCanCreateProfile","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"},{"internalType":"string","name":"flag","type":"string"},{"internalType":"bool","name":"value","type":"bool"}],"name":"setCompetitionFlag","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"},{"internalType":"string","name":"flag","type":"string"},{"internalType":"bool","name":"value","type":"bool"}],"name":"setFeatureFlag","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"flag","type":"string"},{"internalType":"uint256","name":"level","type":"uint256"}],"name":"setFlagLevelRequirement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"key","type":"string"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setNumericData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"open","type":"bool"}],"name":"setOpenProfileCreation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"},{"internalType":"string","name":"flag","type":"string"},{"internalType":"bool","name":"value","type":"bool"},{"internalType":"bytes32","name":"nonce","type":"bytes32"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"setPlayerFlagWithSignature","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"key","type":"string"},{"internalType":"string","name":"value","type":"string"}],"name":"setStringData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newTrustedSigner","type":"address"}],"name":"setTrustedSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"trustedSigner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newAddressManager","type":"address"}],"name":"updateAddressManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"},{"internalType":"address","name":"game","type":"address"},{"internalType":"bool","name":"isWin","type":"bool"}],"name":"updateGameStats","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"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"usedNonces","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60a0806040523461002a5730608052614388908161002f823960805181818161109001526113bd0152f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c80625476641461038e57806301ffc9a714610389578063021fea2c1461038457806309d52a771461037f5780630f53a4701461037a57806319731829146103755780631a429918146103705780631b5a74371461036b57806323dca43914610366578063248a9ca314610361578063259b1a621461035c578063286d0c2c146103575780632f2ff15d1461035257806335a636841461034d57806336568abe14610348578063396533e8146103435780633ab76e9f1461033e5780633e223b84146103395780634b92f7bd146103345780634f1ef2861461032f57806352cc5d721461032a57806352d1902d1461032557806354b478081461032057806355ded5881461031b57806356a1c7011461031657806360c2d994146103115780636aa43c981461030c5780636d6ab8691461030757806375b238fc146103025780638545f34e146102fd5780638679e7fc146102f85780638b736f25146102f357806391d14854146102ee5780639c91aa14146102e9578063a0d0a9d4146102e4578063a217fddf146102df578063a787c80b146102da578063ad3cb1cc146102d5578063b692a77d146102d0578063b90665e514610285578063be4d28a9146102cb578063c1065ae4146102c6578063c47c4bc2146102c1578063c787db3f146102bc578063c8089bbb146102b7578063cf12d07e146102b2578063d547741f146102ad578063d56d229d146102a8578063d861066d146102a3578063d9d147231461029e578063e0bd467914610299578063e4bbb5a814610294578063f72c0d8b1461028f578063f74d54801461028a578063feb61724146102855763ff6fcdbd14610280575f80fd5b612255565b611b58565b6121e8565b6121ae565b61208f565b611ffe565b611f45565b611f05565b611ede565b611e92565b611da0565b611d84565b611cbd565b611c72565b611c38565b611b87565b611b18565b611ad3565b611a24565b611a0a565b6119ca565b611991565b61193f565b611871565b611832565b6116a3565b61167c565b61165b565b6115ee565b6115cf565b6114ec565b61143b565b611414565b6113ab565b6111a3565b61104e565b611014565b610fd6565b610f89565b610f6e565b610f23565b610efd565b610eb1565b610e91565b610d4c565b610ce9565b610bfd565b610b9e565b610b1a565b6107a5565b610672565b610641565b61060e565b6105b8565b6103a1565b5f91031261039d57565b5f80fd5b3461039d575f36600319011261039d576103b96138f0565b60055460ff16801561059b575b801561056a575b15610559575f546103ee906001600160a01b03165b6001600160a01b031690565b60405163785f8ee360e01b8152336004820152602091908281602481855afa908115610516575f9161052c575b5061051b57604051632b39b08560e01b8152336004820152908290829060249082905f905af1918215610516575f926104e9575b826104ac61049a600461046a845f52600260205260405f2090565b5f81555f600182015542600282015561048d60038201600160ff19825416179055565b016104a761049a826122be565b805460ff19166001179055565b6122dc565b337fcb939889322fed25104a5d8945bde5162563412a1fd718946cdcacb03088ea085f80a36104e760015f805160206142f383398151915255565b005b6105089250803d1061050f575b6105008183610719565b8101906122af565b5f8061044f565b503d6104f6565b6122a4565b60405162dc149f60e41b8152600490fd5b61054c9150833d8511610552575b6105448183610719565b81019061228f565b5f61041b565b503d61053a565b6040516282b42960e81b8152600490fd5b5061059661058f3361057a612210565b9060018060a01b03165f5260205260405f2090565b5460ff1690565b6103cd565b50335f9081526004602052604090206105b39061058f565b6103c6565b3461039d57602036600319011261039d5760043563ffffffff60e01b811680910361039d57602090637965db0b60e01b81149081156105fd575b506040519015158152f35b6301ffc9a760e01b1490505f6105f2565b3461039d575f36600319011261039d57602060ff600554166040519015158152f35b6001600160a01b0381160361039d57565b3461039d57602036600319011261039d57602061066860043561066381610630565b6122fe565b6040519015158152f35b3461039d57602036600319011261039d5761010061069a60043561069581610630565b612475565b956040959195949294519788526020880152604087015260608601526080850152151560a0840152151560c0830152151560e0820152f35b634e487b7160e01b5f52604160045260245ffd5b6001600160401b0381116106f957604052565b6106d2565b604081019081106001600160401b038211176106f957604052565b90601f801991011681019081106001600160401b038211176106f957604052565b6001600160401b0381116106f957601f01601f191660200190565b81601f8201121561039d5780359061076c8261073a565b9261077a6040519485610719565b8284526020838301011161039d57815f926020809301838601378301015290565b8015150361039d57565b3461039d5760c036600319011261039d57600480356107c381610630565b6001600160401b039060243582811161039d576107e39036908501610755565b604435906107f08261079b565b6064359360a43590811161039d5761080b9036908701610755565b60018060a01b0394855f5416946040928351809863785f8ee360e01b8252831697888b83015281602460209b8c935afa908115610516575f91610a54575b5015610a44576108576138f0565b845115610a34576108bb6108bf91875f14610a1f57866108b08b6108a261087c6125b0565b61089c61088f8c51968795860190611cfb565b601d60f91b815260010190565b90611cfb565b03601f198101835282610719565b856084359186613ad4565b1590565b610a0f578661091c916108dd61049a855f52600860205260405f2090565b5f546108f1906001600160a01b03166103e2565b85516378a9bb4360e11b81526001600160a01b03909216828c01908152919384928391829160200190565b03915afa968715610516575f976109cd575b5050906109b49161097785610966899a61095f5f805160206142938339815191529a9b5f52600260205260405f2090565b0187611d5e565b9060ff801983541691151516179055565b867f54eedd7fa447672c28684655d4710729f87e9cc7f60c8bc1a67931228de12bdc8351806109a78989836125d0565b0390a351928392836125d0565b0390a36104e760015f805160206142f383398151915255565b5f8051602061429383398151915296975085610966610a016109b49695948461097795903d1061050f576105008183610719565b99985050508192935061092e565b8251638baa579f60e01b81528890fd5b866108b08b6108a2610a2f61258f565b61087c565b835163c52a9bd360e01b81528990fd5b8351636f86092760e01b81528990fd5b610a6b9150893d8b11610552576105448183610719565b5f610849565b5f5b838110610a825750505f910152565b8181015183820152602001610a73565b90602091610aab81518092818552858086019101610a71565b601f01601f1916010190565b6020808201906020835283518092526040830192602060408460051b8301019501935f915b848310610aec5750505050505090565b9091929394958480610b0a600193603f198682030187528a51610a92565b9801930193019194939290610adc565b3461039d57602036600319011261039d57610b4e610b42600435610b3d81610630565b6128c5565b60405191829182610ab7565b0390f35b90604060031983011261039d57600435610b6b81610630565b91602435906001600160401b03821161039d57610b8a91600401610755565b90565b906020610b8a928181520190610a92565b3461039d57610b4e610bb8610bb236610b52565b90612a4e565b604051918291602083526020830190610a92565b604060031982011261039d57600435906001600160401b03821161039d57610bf691600401610755565b9060243590565b3461039d57610c0b36610bcc565b5f5460405163785f8ee360e01b8152336004820152919260209290916001600160a01b0316908381602481855afa908115610516575f91610ccc575b5015610cba5782602491604051928380926378a9bb4360e11b82523360048301525afa92831561051657610c9a93600692610c93925f92610c9d575b50505f52600260205260405f2090565b0190611d5e565b55005b610cb39250803d1061050f576105008183610719565b5f80610c83565b604051636f86092760e01b8152600490fd5b610ce39150843d8611610552576105448183610719565b5f610c47565b3461039d57602036600319011261039d576004355f525f805160206142d38339815191526020526020600160405f200154604051908152f35b602060031982011261039d57600435906001600160401b03821161039d57610b8a91600401610755565b3461039d57610d5a36610d22565b610d62613bc3565b805115610e7f57610d786108bb61058f83611d12565b610e6d575f5b6009549081811015610e6657610d938161276a565b50604090815190610de260209183610dae84820180936127a4565b0393610dc2601f1995868101835282610719565b51902093519182019282610dd6858a611cfb565b03908101835282610719565b51902014610df4576001915001610d7e565b610e12610e0b610e06610e1894612b21565b61276a565b509161276a565b90612c87565b610e20612c96565b610e36610e2c82611d12565b805460ff19169055565b610e403391612d50565b7fc61e2a3bb5bb70280a40b11165c59c641a8c4aa8a34d5235021393abd6e5d5405f80a3005b5050610e20565b604051638403835560e01b8152600490fd5b60405163c52a9bd360e01b8152600490fd5b3461039d57610c9a610ea236610bcc565b9190610eac613bc3565b611d38565b3461039d57604036600319011261039d576104e7602435600435610ed482610630565b805f525f805160206142d3833981519152602052610ef8600160405f200154613cbb565b613f9b565b3461039d57602060ff610f17610f1236610d22565b611d12565b54166040519015158152f35b3461039d57604036600319011261039d57602435610f4081610630565b336001600160a01b03821603610f5c576104e790600435613fd1565b60405163334bd91960e11b8152600490fd5b3461039d575f36600319011261039d57610b4e610b426126e1565b3461039d575f36600319011261039d576001546040516001600160a01b039091168152602090f35b604090600319011261039d57600435610fc981610630565b90602435610b8a8161079b565b3461039d576104e7610fe736610fb1565b90610ff0613bc3565b60018060a01b03165f52600360205260405f209060ff801983541691151516179055565b3461039d575f36600319011261039d5760206040517f8b98aa2c5e8e5cc63efb1164930fb01ca3b7750a60d94f08c39af078758aa67c8152f35b604036600319011261039d57600480359061106882610630565b6024356001600160401b03811161039d576110869036908301610755565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116308114908115611188575b506111775790602083926110ce613c0d565b6040516352d1902d60e01b8152938491829088165afa5f9281611156575b5061111a575050604051634c9c8ce360e01b81526001600160a01b0390921690820190815281906020010390fd5b83835f805160206142b3833981519152840361113a576104e7838361411c565b604051632a87526960e21b815290810184815281906020010390fd5b61117091935060203d60201161050f576105008183610719565b915f6110ec565b60405163703e46dd60e11b81528390fd5b9050815f805160206142b3833981519152541614155f6110bc565b3461039d5760208060031936011261039d5760048035906111c382610630565b6111cb6138f0565b6111dd6108bb61058f3361057a612210565b61139d575f546111f5906001600160a01b03166103e2565b60405163785f8ee360e01b81526001600160a01b0384168382019081528590829081906020010381855afa908115610516575f91611380575b5015611370576040516378a9bb4360e11b81526001600160a01b038416838201908152909185918391908290819060200103915afa938415610516575f94611351575b505061129f611288845f52600260205260405f2090565b60035f918281558260018201558260028201550155565b5f546112b3906001600160a01b03166103e2565b803b1561039d57604051630d41ab1d60e21b81526001600160a01b038416928101928352915f91839182908490829060200103925af1801561051657611338575b506001600160a01b03167fd66d0b9971cb093b581bbb1105187fae2d6d1a45850595fd64de0c1f1a26bdb05f80a36104e760015f805160206142f383398151915255565b8061134561134b926106e6565b80610393565b5f6112f4565b611368929450803d1061050f576105008183610719565b915f80611271565b50604051636f86092760e01b8152fd5b6113979150853d8711610552576105448183610719565b5f61122e565b6040516282b42960e81b8152fd5b3461039d575f36600319011261039d577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031630036114025760206040515f805160206142b38339815191528152f35b60405163703e46dd60e11b8152600490fd5b3461039d57602036600319011261039d57602061066860043561143681610630565b612d70565b3461039d5761144936610d22565b611451613bc3565b805115610e7f5760ff61146382611d12565b54166114da57600954600160401b8110156106f957806001611488920160095561276a565b9190916114d55761149c816114ad93612e6c565b6114a861049a82611d12565b612d50565b33907fe40d53daf1aa500910cda0ffaf09f742fdca4a360ed86263572b9293487f9d385f80a3005b612b2f565b604051634ebf3ce560e11b8152600490fd5b3461039d57602036600319011261039d5760043561150981610630565b611511613bc3565b6001600160a01b039081169081156115785760075490811690818314611566576001600160a01b03191682176007557f4a297cf5d32586f80d2b0708a39d2da1f46e6ae7722171e1c51dfd685b5b8aa85f80a3005b6040516366ac7ca960e01b8152600490fd5b60405163d92e233d60e01b8152600490fd5b90606060031983011261039d576004356115a381610630565b91602435906001600160401b03821161039d576115c291600401610755565b90604435610b8a8161079b565b3461039d576104e76115e03661158a565b916115e9613c64565b612f2f565b3461039d57604036600319011261039d576001600160401b0360043581811161039d573660238201121561039d57806004013582811161039d573660248260051b8401011161039d5760243592831161039d5760246116546104e7943690600401610755565b9201613039565b3461039d57602061167461166e36610b52565b906131c4565b604051908152f35b3461039d575f36600319011261039d5760206040515f805160206143138339815191528152f35b3461039d5760208060031936011261039d5760048035906116c382610630565b6116cb613bc3565b6116d36138f0565b6001600160a01b038216918215611822575f546116f8906001600160a01b03166103e2565b60405163785f8ee360e01b81526001600160a01b0383168482019081529192918690829081906020010381865afa908115610516575f91611805575b506117f557604051632b39b08560e01b81526001600160a01b039091168382019081529091859183919082905f90829060200103925af1938415610516575f946117ce575b505061049a6117949161046a855f52600260205260405f2090565b7fcb939889322fed25104a5d8945bde5162563412a1fd718946cdcacb03088ea085f80a36104e760015f805160206142f383398151915255565b61179492945061049a91816117ee92903d1061050f576105008183610719565b9391611779565b60405162dc149f60e41b81528390fd5b61181c9150863d8811610552576105448183610719565b5f611734565b5060405163d92e233d60e01b8152fd5b3461039d576104e76118433661158a565b916115e9613bc3565b606090600319011261039d5760043561186481610630565b906024356115c281610630565b3461039d5761187f3661184c565b335f9081527fe0f087ff0e85a0e5564f4c99e8ede161bb357dfb3d6d4ae2c96fca5b90059fa3602052604090205490919060ff168015611929575b15610559575f5460405163785f8ee360e01b81526001600160a01b038581166004830152909160209183916024918391165afa908115610516575f9161190a575b5015610cba576104e792613292565b611923915060203d602011610552576105448183610719565b5f6118fb565b50335f52600360205260ff60405f2054166118ba565b3461039d57604036600319011261039d57602060ff610f1760243561196381610630565b6004355f525f805160206142d3833981519152845260405f209060018060a01b03165f5260205260405f2090565b3461039d57602036600319011261039d576004356119ae8161079b565b6119b6613bc3565b60ff80196005541691151516176005555f80f35b3461039d57604036600319011261039d5760406119fe6004356119ec81610630565b602435906119f982610630565b6133ca565b82519182526020820152f35b3461039d575f36600319011261039d5760206040515f8152f35b3461039d57602036600319011261039d57600435611a4181610630565b5f5460405163785f8ee360e01b81526001600160a01b0392831660048201529160209183916024918391165afa801561051657610b4e915f91611a92575b5060405190151581529081906020820190565b611aab915060203d602011610552576105448183610719565b5f611a7f565b60405190602082018281106001600160401b038211176106f9576040525f8252565b3461039d575f36600319011261039d57610b4e604051611af2816106fe565b60058152640352e302e360dc1b6020820152604051918291602083526020830190610a92565b3461039d57602036600319011261039d57600435611b3581610630565b60018060a01b03165f526004602052602060ff60405f2054166040519015158152f35b3461039d57602036600319011261039d576004355f526008602052602060ff60405f2054166040519015158152f35b3461039d57604036600319011261039d576001600160401b0360043581811161039d57611bb8903690600401610755565b9060243590811161039d57611bd1903690600401610755565b5f5460405163785f8ee360e01b815233600482015290602090829060249082906001600160a01b03165afa908115610516575f91611c19575b5015610cba576104e7916134c2565b611c32915060203d602011610552576105448183610719565b5f611c0a565b3461039d575f36600319011261039d5760206040517fa55d967c30eb026b7862f81068874ea0062a1488156f83352b6a6a9230b9df7f8152f35b3461039d57602036600319011261039d57600435611c8f81610630565b611c97613bc3565b6001600160a01b03168015611578576001600160601b0360a01b60015416176001555f80f35b3461039d576104e7611cce36610fb1565b90611cd7613bc3565b60018060a01b03165f52600460205260405f209060ff801983541691151516179055565b90611d0e60209282815194859201610a71565b0190565b6020611d2b918160405193828580945193849201610a71565b8101600a81520301902090565b6020611d51918160405193828580945193849201610a71565b8101600681520301902090565b602090611d78928260405194838680955193849201610a71565b82019081520301902090565b3461039d576020611d97610eac36610d22565b54604051908152f35b3461039d57611dae3661158a565b335f9081527fd70156e933d5bd3a8c5f0a84479b4352fc5b25250b25dbe44989f8ba7712c85b60205260409020549091907fa55d967c30eb026b7862f81068874ea0062a1488156f83352b6a6a9230b9df7f9060ff1615611e7457505f5460405163785f8ee360e01b81526001600160a01b038581166004830152909160209183916024918391165afa908115610516575f91611e55575b5015610cba576104e79261351e565b611e6e915060203d602011610552576105448183610719565b5f611e46565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b3461039d57604036600319011261039d576104e7602435600435611eb582610630565b805f525f805160206142d3833981519152602052611ed9600160405f200154613cbb565b613fd1565b3461039d575f36600319011261039d575f546040516001600160a01b039091168152602090f35b3461039d57602036600319011261039d57600435611f2281610630565b60018060a01b03165f526003602052602060ff60405f2054166040519015158152f35b3461039d57608036600319011261039d576001600160401b0360043581811161039d57611f76903690600401610755565b9060643590811161039d57611f8f903690600401610755565b5f5460405163785f8ee360e01b815233600482015290602090829060249082906001600160a01b03165afa908115610516575f91611fdf575b5015610cba576104e791604435906024359061360a565b611ff8915060203d602011610552576105448183610719565b5f611fc8565b3461039d5761200c36610b52565b5f5460405163785f8ee360e01b81526001600160a01b038481166004830152929392909160209183916024918391165afa908115610516575f91612070575b5015610cba57610b4e9161205e9161371e565b60405190151581529081906020820190565b612089915060203d602011610552576105448183610719565b5f61204b565b3461039d5761209d3661184c565b5f8051602061433383398151915254604081901c60ff16159392906001600160401b0316801590816121a6575b600114908161219c575b159081612193575b50612181575f80516020614333833981519152805467ffffffffffffffff1916600117905561210f928461215d57613799565b61211557005b5f80516020614333833981519152805460ff60401b19169055604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a1005b5f80516020614333833981519152805460ff60401b1916600160401b179055613799565b60405163f92ee8a960e01b8152600490fd5b9050155f6120dc565b303b1591506120d4565b8591506120ca565b3461039d575f36600319011261039d5760206040517f189ab7a9244df0848122154315af71fe140f3db0fe014031783b0946b8c9d2e38152f35b3461039d575f36600319011261039d576007546040516001600160a01b039091168152602090f35b5f805160206143138339815191525f525f805160206142d38339815191526020527fb16e88c42fd4e48df2dd6a2eabd6bc9aec654ec170056b470819f8892cc6431c90565b3461039d575f36600319011261039d5760206040517fef17b1f3c5a412f9c8e887e3abd186802afec7dc657fd3b7002bf87b128dd0b18152f35b9081602091031261039d5751610b8a8161079b565b6040513d5f823e3d90fd5b9081602091031261039d575190565b6028906040519067697341637469766560c01b825260088201522090565b602c90604051906b63616e506c617947616d657360a01b8252600c8201522090565b5f54612312906001600160a01b03166103e2565b60405163785f8ee360e01b81526001600160a01b038316600482015260209291908381602481855afa908115610516575f91612458575b5015612451576040516378a9bb4360e11b81526001600160a01b0383166004820152908390829060249082905afa801561051657612397915f91612434575b505f52600260205260405f2090565b600381015460ff16908161241d575b81612406575b501561240057604051630a968f0160e31b81526001600160a01b039190911660048201528181602481305afa918215610516575f926123ea57505090565b610b8a9250803d10610552576105448183610719565b50505f90565b6124179150600461058f91016122dc565b5f6123ac565b905061242e61058f600483016122be565b906123a6565b61244b9150843d861161050f576105008183610719565b5f612388565b5050505f90565b61246f9150843d8611610552576105448183610719565b5f612349565b5f54612489906001600160a01b03166103e2565b60405163785f8ee360e01b81526001600160a01b03831660048201526020908181602481865afa908115610516575f91612572575b501561255d576040516378a9bb4360e11b81526001600160a01b0384166004820152918190839060249082905afa80156105165761251692612510925f92610c9d5750505f52600260205260405f2090565b91613934565b92908254926001810154926002820154926004612537600385015460ff1690565b93019161255261058f61254c61058f866122be565b946122dc565b919796959493929190565b5050505f905f905f905f905f905f905f905f90565b6125899150823d8411610552576105448183610719565b5f6124be565b6040519061259c826106fe565b600582526466616c736560d81b6020830152565b604051906125bd826106fe565b60048252637472756560e01b6020830152565b906125e8602091949394604084526040840190610a92565b931515910152565b6001600160401b0381116106f95760051b60200190565b90600182811c92168015612635575b602083101461262157565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612616565b9060405191825f825461265181612607565b908184526020946001916001811690815f146126bf5750600114612681575b50505061267f92500383610719565b565b5f90815285812095935091905b8183106126a757505061267f93508201015f8080612670565b8554888401850152948501948794509183019161268e565b9250505061267f94925060ff191682840152151560051b8201015f8080612670565b600954906126ee826125f0565b916126fc6040519384610719565b80835260095f90815260207f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af8186015b84841061273a575050505050565b60018381926127488561263f565b81520192019301929061272c565b634e487b7160e01b5f52603260045260245ffd5b60095481101561279f5760095f527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af01905f90565b612756565b5f929181546127b281612607565b9260019180831690811561280957506001146127cf575b50505050565b9091929394505f5260209060205f20905f915b8583106127f8575050505001905f8080806127c9565b8054858401529183019181016127e2565b60ff191684525050508115159091020191505f8080806127c9565b60209061283792604051938480936127a4565b9081520301902090565b634e487b7160e01b5f52601160045260245ffd5b5f1981146128635760010190565b612841565b90612872826125f0565b61287f6040519182610719565b8281528092612890601f19916125f0565b01905f5b8281106128a057505050565b806060602080938501015201612894565b805182101561279f5760209160051b010190565b5f546128d9906001600160a01b03166103e2565b60405163785f8ee360e01b81526001600160a01b038316600482015260209291908381602481855afa908115610516575f91612a31575b5015612a26576040516378a9bb4360e11b81526001600160a01b039290921660048301528290829060249082905afa90811561051657612960925f92610c9d5750505f52600260205260405f2090565b6009546004909101905f805b8281106129f2575061297d90612868565b915f905f5b838110612990575050505090565b6129a96108bb61058f846129a38561276a565b50612824565b6129b6575b600101612982565b916129ea6001916129cf6129c98661276a565b5061263f565b6129d982896128b1565b526129e481886128b1565b50612855565b9290506129ae565b612a056108bb61058f866129a38561276a565b612a12575b60010161296c565b90612a1e600191612855565b919050612a0a565b505050610b8a6126e1565b612a489150843d8611610552576105448183610719565b5f612910565b5f54612a62906001600160a01b03166103e2565b60405163785f8ee360e01b81526001600160a01b038316600482015260209391908481602481855afa908115610516575f91612b04575b5015612af8576040516378a9bb4360e11b81526001600160a01b039390931660048401528390839060249082905afa91821561051657610c93612af393600592610b8a965f92610c9d5750505f52600260205260405f2090565b61263f565b50505050610b8a611ab1565b612b1b9150853d8711610552576105448183610719565b5f612a99565b5f1981019190821161286357565b634e487b7160e01b5f525f60045260245ffd5b818110612b4d575050565b5f8155600101612b42565b9190601f8111612b6757505050565b61267f925f5260205f20906020601f840160051c83019310612b91575b601f0160051c0190612b42565b9091508190612b84565b919091828114612c8257612baf8354612607565b6001600160401b0381116106f957612bd181612bcb8454612607565b84612b58565b5f93601f8211600114612c1057612c0192939482915f92612c05575b50508160011b915f199060031b1c19161790565b9055565b015490505f80612bed565b612c22601f198316915f5260205f2090565b94612c30845f5260205f2090565b915f5b818110612c6a57509583600195969710612c52575b505050811b019055565b01545f1960f88460031b161c191690555f8080612c48565b87830154845560019384019390920191602001612c33565b509050565b91906114d55761267f91612b9b565b6009548015612d3c575f198101908082101561279f577f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7ae9060095f5201612cdd8154612607565b9081612ceb575b5050600955565b601f8211600114612d02575f9150555b5f80612ce4565b612d28612d37926001601f612d1a855f5260205f2090565b920160051c82019101612b42565b5f908082528160208120915555565b612cfb565b634e487b7160e01b5f52603160045260245ffd5b612d6890602060405192828480945193849201610a71565b810103902090565b5f54612d84906001600160a01b03166103e2565b60405163785f8ee360e01b81526001600160a01b038316600482015260209291908381602481855afa908115610516575f91612e4f575b5015612451576040516378a9bb4360e11b81526001600160a01b039290921660048301528290829060249082905afa90811561051657612e0b925f92610c9d5750505f52600260205260405f2090565b6009548015612e4857906004015f5b828110612e2957505050600190565b612e3c6108bb61058f846129a38561276a565b61245157600101612e1a565b5050600190565b612e669150843d8611610552576105448183610719565b5f612dbb565b91909182516001600160401b0381116106f957612e8d81612bcb8454612607565b602080601f8311600114612ec957508190612c019394955f92612ebe5750508160011b915f199060031b1c19161790565b015190505f80612bed565b90601f19831695612edd855f5260205f2090565b925f905b888210612f1757505083600195969710612eff57505050811b019055565b01515f1960f88460031b161c191690555f8080612c48565b80600185968294968601518155019501930190612ee1565b5f5460405163785f8ee360e01b81526001600160a01b039283166004820181905260209590949093909216908581602481855afa908115610516575f9161301c575b5015610cba5784602491604051928380926378a9bb4360e11b82528860048301525afa948515610516575f95612fea575b5050905f8051602061429383398151915291612fd6826109666004612fcf895f52600260205260405f2090565b0184611d5e565b612fe5604051928392836125d0565b0390a3565b5f8051602061429383398151915293929550908161301392903d1061050f576105008183610719565b9390915f612fa2565b6130339150863d8811610552576105448183610719565b5f612f71565b9190613043613c64565b5f5b8181106130525750505050565b5f54613066906001600160a01b03166103e2565b9061307a6130758285886131aa565b6131ba565b6040805163785f8ee360e01b81526001600160a01b039290921660048084019190915260209491929091908581602481855afa908115610516575f9161318d575b506130ce575b5050506001915001613045565b8461310d916130e1613075878a8d6131aa565b85516378a9bb4360e11b81526001600160a01b0390911685820190815290938492918391829160200190565b03915afa9182156105165761095f61313c9361049a936001985f92610c9d5750505f52600260205260405f2090565b61314a6130758386896131aa565b90517f06980050ce82d9f5a04e1ccb24535c222546e895d0d62b42154bd4241643cdfd3392858060a01b031691806131828982610b8d565b0390a35f80806130c1565b6131a49150863d8811610552576105448183610719565b5f6130bb565b919081101561279f5760051b0190565b35610b8a81610630565b5f549091906131db906001600160a01b03166103e2565b60405163785f8ee360e01b81526001600160a01b038416600482015260209391908481602481855afa908115610516575f91613275575b501561326d576040516378a9bb4360e11b81526001600160a01b039290921660048301528390829060249082905afa9283156105165761326993600692610c93925f92610c9d5750505f52600260205260405f2090565b5490565b505050505f90565b61328c9150853d8711610552576105448183610719565b5f613212565b5f54909291906132aa906001600160a01b03166103e2565b6040516378a9bb4360e11b81526001600160a01b03851660048201529190602090839060249082905afa918215610516577f6cf8224f35649bfbde77564cffb28be053bad6e412099ee796e956aff075fd5892613316915f916133b157505f52600260205260405f2090565b6133208154612855565b81556001600160a01b0382165f9081526007820160205260409020829085906133498154612855565b9055613372575b505060405192151583526001600160a01b039081169316918060208101612fe5565b600882600161339e94016133868154612855565b9055019060018060a01b03165f5260205260405f2090565b6133a88154612855565b90555f81613350565b61244b915060203d60201161050f576105008183610719565b5f549192916133e1906001600160a01b03166103e2565b60405163785f8ee360e01b81526001600160a01b038316600482015260209291908381602481855afa908115610516575f916134a5575b501561349a576040516378a9bb4360e11b81526001600160a01b039290921660048301529091908190839060249082905afa8015610516576132699261346e925f92610c9d5750505f52600260205260405f2090565b6001600160a01b03939093165f9081526007840160209081526040808320546008909601909152902090565b50505090505f905f90565b6134bc9150843d8611610552576105448183610719565b5f613418565b5f546040516378a9bb4360e11b815233600482015291929190602090829060249082906001600160a01b03165afa928315610516576005610c9361267f95613519945f916133b157505f52600260205260405f2090565b612e6c565b5f54613532906001600160a01b03166103e2565b6040516378a9bb4360e11b81526001600160a01b03831660048201529390602090859060249082905afa938415610516575f946135e9575b5061357483611d38565b546135bc575b5f80516020614293833981519152916135a482610966600461095f895f52600260205260405f2090565b612fe560405192839260018060a01b031695836125d0565b6135c582613934565b90506135d084611d38565b54111561357a576040516313fee0df60e21b8152600490fd5b61360391945060203d60201161050f576105008183610719565b925f61356a565b919290926136166138f0565b825115610e7f5761362d916108bb91858533613ad4565b61370c5761364661049a835f52600860205260405f2090565b5f5461365a906001600160a01b03166103e2565b6040516378a9bb4360e11b815233600482015290602090829060249082905afa80156105165761351960056136a385936136c3955f916133b157505f52600260205260405f2090565b016029906040519068191a5cd8dbdc99125960ba1b825260098201522090565b7f4bf5242860a031b49a24efb84dcf6766de2984a59b84b1210026e80b5a53121c604051806136f3339482610b8d565b0390a361267f60015f805160206142f383398151915255565b604051638baa579f60e01b8152600490fd5b5f546040516378a9bb4360e11b81526001600160a01b0392831660048201529160209183916024918391165afa9182156105165760ff92613775925f9161377a575b505f526002602052600460405f200190611d5e565b541690565b613793915060203d60201161050f576105008183610719565b5f613760565b6001600160a01b039081169291831561157857169182156115785761382d9261381b916137c46141c1565b6137cc6141c1565b6137d46141c1565b60015f805160206142f3833981519152556137ed6141c1565b6001600160601b0360a01b5f5416175f5560018060a01b03166001600160601b0360a01b6001541617600155565b60ff8019600554169115151617600555565b61383633613ce7565b5061384033613d92565b5061384a33613e4e565b5061385433613ebd565b5061385e33613f2c565b5061267f600a602e6040516d2b22aa22a920a72fa82620aca2a960911b81526006600e82015220556019602d6040516c22ac2822a92a2fa82620aca2a960991b81526006600d82015220556032602d6040516c26a0a9aa22a92fa82620aca2a960991b81526006600d82015220556014602c6040516b23aaa4a6222fa622a0a222a960a11b81526006600c8201522055565b5f805160206142f3833981519152600281541461390d5760029055565b604051633ee5aeb560e01b8152600490fd5b9081602091031261039d5751610b8a81610630565b60015490919061394c906001600160a01b03166103e2565b60405163bf40fac160e01b8152602060048201819052601260248301527122ac2822a924a2a721a2afa6a0a720a3a2a960711b604483015293918490829060649082905afa908115610516575f91613a6e575b506001600160a01b031690816139bb575b505090505f90600190565b6040516319522d9f60e01b81526001600160a01b0382166004820152918483602481845afa5f9381613a4f575b506139f357506139b0565b6040516334b735a160e01b81526001600160a01b039290921660048301528490829060249082905afa938415610516575f94613a30575b50509190565b613a47929450803d1061050f576105008183610719565b915f80613a2a565b613a67919450863d881161050f576105008183610719565b925f6139e8565b613a8e9150843d8611613a94575b613a868183610719565b81019061391f565b5f61399f565b503d613a7c565b60549492916001600160601b03199060601b168152613ac4825180936020601485019101610a71565b0191601483015260348201520190565b6007546001600160a01b039081169590949390929091908615613bb157844211613b9f57613b0d61058f845f52600860205260405f2090565b613b8e57613b29613b8995604051948593602085019586613a9b565b0391613b3d601f1993848101835282610719565b51902090613b816040519182610dd6602082019586603c917f19457468657265756d205369676e6564204d6573736167653a0a3332000000008252601c8201520190565b519020614075565b161490565b604051623f613760e71b8152600490fd5b604051630819bdcd60e01b8152600490fd5b604051630aeae73760e21b8152600490fd5b335f9081527fb16e88c42fd4e48df2dd6a2eabd6bc9aec654ec170056b470819f8892cc6431c602052604090205f805160206143138339815191529060ff905b541615611e745750565b335f9081527fab71e3f32666744d246edff3f96e4bdafee2e9867098cdd118a979a7464786a8602052604090207f189ab7a9244df0848122154315af71fe140f3db0fe014031783b0946b8c9d2e39060ff90613c03565b335f9081527f72cd09738b13b8f37d6f829e3529e6c68fe42286505d3d75fc989cd7172dfc0c602052604090207f8b98aa2c5e8e5cc63efb1164930fb01ca3b7750a60d94f08c39af078758aa67c9060ff90613c03565b5f8181525f805160206142d383398151915260209081526040808320338452909152902060ff90613c03565b6001600160a01b0381165f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d60205260409020545f805160206142d38339815191529060ff16612400575f808052602091825260408082206001600160a01b038516835290925220805460ff1916600117905533906001600160a01b03165f7f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b6001600160a01b0381165f9081527fb16e88c42fd4e48df2dd6a2eabd6bc9aec654ec170056b470819f8892cc6431c602052604090205f80516020614313833981519152905f805160206142d38339815191529060ff905b5416612451575f828152602091825260408082206001600160a01b038616835290925220805460ff1916600117905533916001600160a01b0316907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b6001600160a01b0381165f9081527fab71e3f32666744d246edff3f96e4bdafee2e9867098cdd118a979a7464786a8602052604090207f189ab7a9244df0848122154315af71fe140f3db0fe014031783b0946b8c9d2e3905f805160206142d38339815191529060ff90613dea565b6001600160a01b0381165f9081527fe0f087ff0e85a0e5564f4c99e8ede161bb357dfb3d6d4ae2c96fca5b90059fa3602052604090207fef17b1f3c5a412f9c8e887e3abd186802afec7dc657fd3b7002bf87b128dd0b1905f805160206142d38339815191529060ff90613dea565b6001600160a01b0381165f9081527f72cd09738b13b8f37d6f829e3529e6c68fe42286505d3d75fc989cd7172dfc0c602052604090207f8b98aa2c5e8e5cc63efb1164930fb01ca3b7750a60d94f08c39af078758aa67c905f805160206142d38339815191529060ff90613dea565b5f8181525f805160206142d3833981519152602081815260408084206001600160a01b038716855290915290912060ff90613dea565b5f8181525f805160206142d3833981519152602081815260408084206001600160a01b03871685529091529091205460ff1615612451575f828152602091825260408082206001600160a01b038616835290925220805460ff1916905533916001600160a01b0316907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b60ff601b9116019060ff821161286357565b90604181510361370c57602081015191606060408301519201515f1a90601b821061410c575b60ff8216601b8114159081614100575b5061370c576140de5f93602095604051948594859094939260ff6060936080840197845216602083015260408201520152565b838052039060015afa15610516575f51906001600160a01b0382161561370c57565b601c915014155f6140ab565b9061411690614063565b9061409b565b90813b156141a0575f805160206142b383398151915280546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a280511561418557614182916141ef565b50565b50503461418e57565b60405163b398979f60e01b8152600490fd5b604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b60ff5f805160206143338339815191525460401c16156141dd57565b604051631afcd79f60e31b8152600490fd5b5f80610b8a93602081519101845af43d1561422b573d9161420f8361073a565b9261421d6040519485610719565b83523d5f602085013e61422f565b6060915b90614256575080511561424457602081519101fd5b60405163d6bda27560e01b8152600490fd5b81511580614289575b614267575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b1561425f56fe8f3a324e5e9460b7562b6201535d730ee10ee7b35b28067e88cc44d5fc6f1402360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268009b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a2646970667358221220962b9943f339b47d30d5ffc06cbd929819c34fb066243b102d8a06244ddf30b864736f6c63430008180033
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f3560e01c80625476641461038e57806301ffc9a714610389578063021fea2c1461038457806309d52a771461037f5780630f53a4701461037a57806319731829146103755780631a429918146103705780631b5a74371461036b57806323dca43914610366578063248a9ca314610361578063259b1a621461035c578063286d0c2c146103575780632f2ff15d1461035257806335a636841461034d57806336568abe14610348578063396533e8146103435780633ab76e9f1461033e5780633e223b84146103395780634b92f7bd146103345780634f1ef2861461032f57806352cc5d721461032a57806352d1902d1461032557806354b478081461032057806355ded5881461031b57806356a1c7011461031657806360c2d994146103115780636aa43c981461030c5780636d6ab8691461030757806375b238fc146103025780638545f34e146102fd5780638679e7fc146102f85780638b736f25146102f357806391d14854146102ee5780639c91aa14146102e9578063a0d0a9d4146102e4578063a217fddf146102df578063a787c80b146102da578063ad3cb1cc146102d5578063b692a77d146102d0578063b90665e514610285578063be4d28a9146102cb578063c1065ae4146102c6578063c47c4bc2146102c1578063c787db3f146102bc578063c8089bbb146102b7578063cf12d07e146102b2578063d547741f146102ad578063d56d229d146102a8578063d861066d146102a3578063d9d147231461029e578063e0bd467914610299578063e4bbb5a814610294578063f72c0d8b1461028f578063f74d54801461028a578063feb61724146102855763ff6fcdbd14610280575f80fd5b612255565b611b58565b6121e8565b6121ae565b61208f565b611ffe565b611f45565b611f05565b611ede565b611e92565b611da0565b611d84565b611cbd565b611c72565b611c38565b611b87565b611b18565b611ad3565b611a24565b611a0a565b6119ca565b611991565b61193f565b611871565b611832565b6116a3565b61167c565b61165b565b6115ee565b6115cf565b6114ec565b61143b565b611414565b6113ab565b6111a3565b61104e565b611014565b610fd6565b610f89565b610f6e565b610f23565b610efd565b610eb1565b610e91565b610d4c565b610ce9565b610bfd565b610b9e565b610b1a565b6107a5565b610672565b610641565b61060e565b6105b8565b6103a1565b5f91031261039d57565b5f80fd5b3461039d575f36600319011261039d576103b96138f0565b60055460ff16801561059b575b801561056a575b15610559575f546103ee906001600160a01b03165b6001600160a01b031690565b60405163785f8ee360e01b8152336004820152602091908281602481855afa908115610516575f9161052c575b5061051b57604051632b39b08560e01b8152336004820152908290829060249082905f905af1918215610516575f926104e9575b826104ac61049a600461046a845f52600260205260405f2090565b5f81555f600182015542600282015561048d60038201600160ff19825416179055565b016104a761049a826122be565b805460ff19166001179055565b6122dc565b337fcb939889322fed25104a5d8945bde5162563412a1fd718946cdcacb03088ea085f80a36104e760015f805160206142f383398151915255565b005b6105089250803d1061050f575b6105008183610719565b8101906122af565b5f8061044f565b503d6104f6565b6122a4565b60405162dc149f60e41b8152600490fd5b61054c9150833d8511610552575b6105448183610719565b81019061228f565b5f61041b565b503d61053a565b6040516282b42960e81b8152600490fd5b5061059661058f3361057a612210565b9060018060a01b03165f5260205260405f2090565b5460ff1690565b6103cd565b50335f9081526004602052604090206105b39061058f565b6103c6565b3461039d57602036600319011261039d5760043563ffffffff60e01b811680910361039d57602090637965db0b60e01b81149081156105fd575b506040519015158152f35b6301ffc9a760e01b1490505f6105f2565b3461039d575f36600319011261039d57602060ff600554166040519015158152f35b6001600160a01b0381160361039d57565b3461039d57602036600319011261039d57602061066860043561066381610630565b6122fe565b6040519015158152f35b3461039d57602036600319011261039d5761010061069a60043561069581610630565b612475565b956040959195949294519788526020880152604087015260608601526080850152151560a0840152151560c0830152151560e0820152f35b634e487b7160e01b5f52604160045260245ffd5b6001600160401b0381116106f957604052565b6106d2565b604081019081106001600160401b038211176106f957604052565b90601f801991011681019081106001600160401b038211176106f957604052565b6001600160401b0381116106f957601f01601f191660200190565b81601f8201121561039d5780359061076c8261073a565b9261077a6040519485610719565b8284526020838301011161039d57815f926020809301838601378301015290565b8015150361039d57565b3461039d5760c036600319011261039d57600480356107c381610630565b6001600160401b039060243582811161039d576107e39036908501610755565b604435906107f08261079b565b6064359360a43590811161039d5761080b9036908701610755565b60018060a01b0394855f5416946040928351809863785f8ee360e01b8252831697888b83015281602460209b8c935afa908115610516575f91610a54575b5015610a44576108576138f0565b845115610a34576108bb6108bf91875f14610a1f57866108b08b6108a261087c6125b0565b61089c61088f8c51968795860190611cfb565b601d60f91b815260010190565b90611cfb565b03601f198101835282610719565b856084359186613ad4565b1590565b610a0f578661091c916108dd61049a855f52600860205260405f2090565b5f546108f1906001600160a01b03166103e2565b85516378a9bb4360e11b81526001600160a01b03909216828c01908152919384928391829160200190565b03915afa968715610516575f976109cd575b5050906109b49161097785610966899a61095f5f805160206142938339815191529a9b5f52600260205260405f2090565b0187611d5e565b9060ff801983541691151516179055565b867f54eedd7fa447672c28684655d4710729f87e9cc7f60c8bc1a67931228de12bdc8351806109a78989836125d0565b0390a351928392836125d0565b0390a36104e760015f805160206142f383398151915255565b5f8051602061429383398151915296975085610966610a016109b49695948461097795903d1061050f576105008183610719565b99985050508192935061092e565b8251638baa579f60e01b81528890fd5b866108b08b6108a2610a2f61258f565b61087c565b835163c52a9bd360e01b81528990fd5b8351636f86092760e01b81528990fd5b610a6b9150893d8b11610552576105448183610719565b5f610849565b5f5b838110610a825750505f910152565b8181015183820152602001610a73565b90602091610aab81518092818552858086019101610a71565b601f01601f1916010190565b6020808201906020835283518092526040830192602060408460051b8301019501935f915b848310610aec5750505050505090565b9091929394958480610b0a600193603f198682030187528a51610a92565b9801930193019194939290610adc565b3461039d57602036600319011261039d57610b4e610b42600435610b3d81610630565b6128c5565b60405191829182610ab7565b0390f35b90604060031983011261039d57600435610b6b81610630565b91602435906001600160401b03821161039d57610b8a91600401610755565b90565b906020610b8a928181520190610a92565b3461039d57610b4e610bb8610bb236610b52565b90612a4e565b604051918291602083526020830190610a92565b604060031982011261039d57600435906001600160401b03821161039d57610bf691600401610755565b9060243590565b3461039d57610c0b36610bcc565b5f5460405163785f8ee360e01b8152336004820152919260209290916001600160a01b0316908381602481855afa908115610516575f91610ccc575b5015610cba5782602491604051928380926378a9bb4360e11b82523360048301525afa92831561051657610c9a93600692610c93925f92610c9d575b50505f52600260205260405f2090565b0190611d5e565b55005b610cb39250803d1061050f576105008183610719565b5f80610c83565b604051636f86092760e01b8152600490fd5b610ce39150843d8611610552576105448183610719565b5f610c47565b3461039d57602036600319011261039d576004355f525f805160206142d38339815191526020526020600160405f200154604051908152f35b602060031982011261039d57600435906001600160401b03821161039d57610b8a91600401610755565b3461039d57610d5a36610d22565b610d62613bc3565b805115610e7f57610d786108bb61058f83611d12565b610e6d575f5b6009549081811015610e6657610d938161276a565b50604090815190610de260209183610dae84820180936127a4565b0393610dc2601f1995868101835282610719565b51902093519182019282610dd6858a611cfb565b03908101835282610719565b51902014610df4576001915001610d7e565b610e12610e0b610e06610e1894612b21565b61276a565b509161276a565b90612c87565b610e20612c96565b610e36610e2c82611d12565b805460ff19169055565b610e403391612d50565b7fc61e2a3bb5bb70280a40b11165c59c641a8c4aa8a34d5235021393abd6e5d5405f80a3005b5050610e20565b604051638403835560e01b8152600490fd5b60405163c52a9bd360e01b8152600490fd5b3461039d57610c9a610ea236610bcc565b9190610eac613bc3565b611d38565b3461039d57604036600319011261039d576104e7602435600435610ed482610630565b805f525f805160206142d3833981519152602052610ef8600160405f200154613cbb565b613f9b565b3461039d57602060ff610f17610f1236610d22565b611d12565b54166040519015158152f35b3461039d57604036600319011261039d57602435610f4081610630565b336001600160a01b03821603610f5c576104e790600435613fd1565b60405163334bd91960e11b8152600490fd5b3461039d575f36600319011261039d57610b4e610b426126e1565b3461039d575f36600319011261039d576001546040516001600160a01b039091168152602090f35b604090600319011261039d57600435610fc981610630565b90602435610b8a8161079b565b3461039d576104e7610fe736610fb1565b90610ff0613bc3565b60018060a01b03165f52600360205260405f209060ff801983541691151516179055565b3461039d575f36600319011261039d5760206040517f8b98aa2c5e8e5cc63efb1164930fb01ca3b7750a60d94f08c39af078758aa67c8152f35b604036600319011261039d57600480359061106882610630565b6024356001600160401b03811161039d576110869036908301610755565b6001600160a01b037f000000000000000000000000aa2670f14c3ca248346ed77daf747d73684159a18116308114908115611188575b506111775790602083926110ce613c0d565b6040516352d1902d60e01b8152938491829088165afa5f9281611156575b5061111a575050604051634c9c8ce360e01b81526001600160a01b0390921690820190815281906020010390fd5b83835f805160206142b3833981519152840361113a576104e7838361411c565b604051632a87526960e21b815290810184815281906020010390fd5b61117091935060203d60201161050f576105008183610719565b915f6110ec565b60405163703e46dd60e11b81528390fd5b9050815f805160206142b3833981519152541614155f6110bc565b3461039d5760208060031936011261039d5760048035906111c382610630565b6111cb6138f0565b6111dd6108bb61058f3361057a612210565b61139d575f546111f5906001600160a01b03166103e2565b60405163785f8ee360e01b81526001600160a01b0384168382019081528590829081906020010381855afa908115610516575f91611380575b5015611370576040516378a9bb4360e11b81526001600160a01b038416838201908152909185918391908290819060200103915afa938415610516575f94611351575b505061129f611288845f52600260205260405f2090565b60035f918281558260018201558260028201550155565b5f546112b3906001600160a01b03166103e2565b803b1561039d57604051630d41ab1d60e21b81526001600160a01b038416928101928352915f91839182908490829060200103925af1801561051657611338575b506001600160a01b03167fd66d0b9971cb093b581bbb1105187fae2d6d1a45850595fd64de0c1f1a26bdb05f80a36104e760015f805160206142f383398151915255565b8061134561134b926106e6565b80610393565b5f6112f4565b611368929450803d1061050f576105008183610719565b915f80611271565b50604051636f86092760e01b8152fd5b6113979150853d8711610552576105448183610719565b5f61122e565b6040516282b42960e81b8152fd5b3461039d575f36600319011261039d577f000000000000000000000000aa2670f14c3ca248346ed77daf747d73684159a16001600160a01b031630036114025760206040515f805160206142b38339815191528152f35b60405163703e46dd60e11b8152600490fd5b3461039d57602036600319011261039d57602061066860043561143681610630565b612d70565b3461039d5761144936610d22565b611451613bc3565b805115610e7f5760ff61146382611d12565b54166114da57600954600160401b8110156106f957806001611488920160095561276a565b9190916114d55761149c816114ad93612e6c565b6114a861049a82611d12565b612d50565b33907fe40d53daf1aa500910cda0ffaf09f742fdca4a360ed86263572b9293487f9d385f80a3005b612b2f565b604051634ebf3ce560e11b8152600490fd5b3461039d57602036600319011261039d5760043561150981610630565b611511613bc3565b6001600160a01b039081169081156115785760075490811690818314611566576001600160a01b03191682176007557f4a297cf5d32586f80d2b0708a39d2da1f46e6ae7722171e1c51dfd685b5b8aa85f80a3005b6040516366ac7ca960e01b8152600490fd5b60405163d92e233d60e01b8152600490fd5b90606060031983011261039d576004356115a381610630565b91602435906001600160401b03821161039d576115c291600401610755565b90604435610b8a8161079b565b3461039d576104e76115e03661158a565b916115e9613c64565b612f2f565b3461039d57604036600319011261039d576001600160401b0360043581811161039d573660238201121561039d57806004013582811161039d573660248260051b8401011161039d5760243592831161039d5760246116546104e7943690600401610755565b9201613039565b3461039d57602061167461166e36610b52565b906131c4565b604051908152f35b3461039d575f36600319011261039d5760206040515f805160206143138339815191528152f35b3461039d5760208060031936011261039d5760048035906116c382610630565b6116cb613bc3565b6116d36138f0565b6001600160a01b038216918215611822575f546116f8906001600160a01b03166103e2565b60405163785f8ee360e01b81526001600160a01b0383168482019081529192918690829081906020010381865afa908115610516575f91611805575b506117f557604051632b39b08560e01b81526001600160a01b039091168382019081529091859183919082905f90829060200103925af1938415610516575f946117ce575b505061049a6117949161046a855f52600260205260405f2090565b7fcb939889322fed25104a5d8945bde5162563412a1fd718946cdcacb03088ea085f80a36104e760015f805160206142f383398151915255565b61179492945061049a91816117ee92903d1061050f576105008183610719565b9391611779565b60405162dc149f60e41b81528390fd5b61181c9150863d8811610552576105448183610719565b5f611734565b5060405163d92e233d60e01b8152fd5b3461039d576104e76118433661158a565b916115e9613bc3565b606090600319011261039d5760043561186481610630565b906024356115c281610630565b3461039d5761187f3661184c565b335f9081527fe0f087ff0e85a0e5564f4c99e8ede161bb357dfb3d6d4ae2c96fca5b90059fa3602052604090205490919060ff168015611929575b15610559575f5460405163785f8ee360e01b81526001600160a01b038581166004830152909160209183916024918391165afa908115610516575f9161190a575b5015610cba576104e792613292565b611923915060203d602011610552576105448183610719565b5f6118fb565b50335f52600360205260ff60405f2054166118ba565b3461039d57604036600319011261039d57602060ff610f1760243561196381610630565b6004355f525f805160206142d3833981519152845260405f209060018060a01b03165f5260205260405f2090565b3461039d57602036600319011261039d576004356119ae8161079b565b6119b6613bc3565b60ff80196005541691151516176005555f80f35b3461039d57604036600319011261039d5760406119fe6004356119ec81610630565b602435906119f982610630565b6133ca565b82519182526020820152f35b3461039d575f36600319011261039d5760206040515f8152f35b3461039d57602036600319011261039d57600435611a4181610630565b5f5460405163785f8ee360e01b81526001600160a01b0392831660048201529160209183916024918391165afa801561051657610b4e915f91611a92575b5060405190151581529081906020820190565b611aab915060203d602011610552576105448183610719565b5f611a7f565b60405190602082018281106001600160401b038211176106f9576040525f8252565b3461039d575f36600319011261039d57610b4e604051611af2816106fe565b60058152640352e302e360dc1b6020820152604051918291602083526020830190610a92565b3461039d57602036600319011261039d57600435611b3581610630565b60018060a01b03165f526004602052602060ff60405f2054166040519015158152f35b3461039d57602036600319011261039d576004355f526008602052602060ff60405f2054166040519015158152f35b3461039d57604036600319011261039d576001600160401b0360043581811161039d57611bb8903690600401610755565b9060243590811161039d57611bd1903690600401610755565b5f5460405163785f8ee360e01b815233600482015290602090829060249082906001600160a01b03165afa908115610516575f91611c19575b5015610cba576104e7916134c2565b611c32915060203d602011610552576105448183610719565b5f611c0a565b3461039d575f36600319011261039d5760206040517fa55d967c30eb026b7862f81068874ea0062a1488156f83352b6a6a9230b9df7f8152f35b3461039d57602036600319011261039d57600435611c8f81610630565b611c97613bc3565b6001600160a01b03168015611578576001600160601b0360a01b60015416176001555f80f35b3461039d576104e7611cce36610fb1565b90611cd7613bc3565b60018060a01b03165f52600460205260405f209060ff801983541691151516179055565b90611d0e60209282815194859201610a71565b0190565b6020611d2b918160405193828580945193849201610a71565b8101600a81520301902090565b6020611d51918160405193828580945193849201610a71565b8101600681520301902090565b602090611d78928260405194838680955193849201610a71565b82019081520301902090565b3461039d576020611d97610eac36610d22565b54604051908152f35b3461039d57611dae3661158a565b335f9081527fd70156e933d5bd3a8c5f0a84479b4352fc5b25250b25dbe44989f8ba7712c85b60205260409020549091907fa55d967c30eb026b7862f81068874ea0062a1488156f83352b6a6a9230b9df7f9060ff1615611e7457505f5460405163785f8ee360e01b81526001600160a01b038581166004830152909160209183916024918391165afa908115610516575f91611e55575b5015610cba576104e79261351e565b611e6e915060203d602011610552576105448183610719565b5f611e46565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b3461039d57604036600319011261039d576104e7602435600435611eb582610630565b805f525f805160206142d3833981519152602052611ed9600160405f200154613cbb565b613fd1565b3461039d575f36600319011261039d575f546040516001600160a01b039091168152602090f35b3461039d57602036600319011261039d57600435611f2281610630565b60018060a01b03165f526003602052602060ff60405f2054166040519015158152f35b3461039d57608036600319011261039d576001600160401b0360043581811161039d57611f76903690600401610755565b9060643590811161039d57611f8f903690600401610755565b5f5460405163785f8ee360e01b815233600482015290602090829060249082906001600160a01b03165afa908115610516575f91611fdf575b5015610cba576104e791604435906024359061360a565b611ff8915060203d602011610552576105448183610719565b5f611fc8565b3461039d5761200c36610b52565b5f5460405163785f8ee360e01b81526001600160a01b038481166004830152929392909160209183916024918391165afa908115610516575f91612070575b5015610cba57610b4e9161205e9161371e565b60405190151581529081906020820190565b612089915060203d602011610552576105448183610719565b5f61204b565b3461039d5761209d3661184c565b5f8051602061433383398151915254604081901c60ff16159392906001600160401b0316801590816121a6575b600114908161219c575b159081612193575b50612181575f80516020614333833981519152805467ffffffffffffffff1916600117905561210f928461215d57613799565b61211557005b5f80516020614333833981519152805460ff60401b19169055604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a1005b5f80516020614333833981519152805460ff60401b1916600160401b179055613799565b60405163f92ee8a960e01b8152600490fd5b9050155f6120dc565b303b1591506120d4565b8591506120ca565b3461039d575f36600319011261039d5760206040517f189ab7a9244df0848122154315af71fe140f3db0fe014031783b0946b8c9d2e38152f35b3461039d575f36600319011261039d576007546040516001600160a01b039091168152602090f35b5f805160206143138339815191525f525f805160206142d38339815191526020527fb16e88c42fd4e48df2dd6a2eabd6bc9aec654ec170056b470819f8892cc6431c90565b3461039d575f36600319011261039d5760206040517fef17b1f3c5a412f9c8e887e3abd186802afec7dc657fd3b7002bf87b128dd0b18152f35b9081602091031261039d5751610b8a8161079b565b6040513d5f823e3d90fd5b9081602091031261039d575190565b6028906040519067697341637469766560c01b825260088201522090565b602c90604051906b63616e506c617947616d657360a01b8252600c8201522090565b5f54612312906001600160a01b03166103e2565b60405163785f8ee360e01b81526001600160a01b038316600482015260209291908381602481855afa908115610516575f91612458575b5015612451576040516378a9bb4360e11b81526001600160a01b0383166004820152908390829060249082905afa801561051657612397915f91612434575b505f52600260205260405f2090565b600381015460ff16908161241d575b81612406575b501561240057604051630a968f0160e31b81526001600160a01b039190911660048201528181602481305afa918215610516575f926123ea57505090565b610b8a9250803d10610552576105448183610719565b50505f90565b6124179150600461058f91016122dc565b5f6123ac565b905061242e61058f600483016122be565b906123a6565b61244b9150843d861161050f576105008183610719565b5f612388565b5050505f90565b61246f9150843d8611610552576105448183610719565b5f612349565b5f54612489906001600160a01b03166103e2565b60405163785f8ee360e01b81526001600160a01b03831660048201526020908181602481865afa908115610516575f91612572575b501561255d576040516378a9bb4360e11b81526001600160a01b0384166004820152918190839060249082905afa80156105165761251692612510925f92610c9d5750505f52600260205260405f2090565b91613934565b92908254926001810154926002820154926004612537600385015460ff1690565b93019161255261058f61254c61058f866122be565b946122dc565b919796959493929190565b5050505f905f905f905f905f905f905f905f90565b6125899150823d8411610552576105448183610719565b5f6124be565b6040519061259c826106fe565b600582526466616c736560d81b6020830152565b604051906125bd826106fe565b60048252637472756560e01b6020830152565b906125e8602091949394604084526040840190610a92565b931515910152565b6001600160401b0381116106f95760051b60200190565b90600182811c92168015612635575b602083101461262157565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612616565b9060405191825f825461265181612607565b908184526020946001916001811690815f146126bf5750600114612681575b50505061267f92500383610719565b565b5f90815285812095935091905b8183106126a757505061267f93508201015f8080612670565b8554888401850152948501948794509183019161268e565b9250505061267f94925060ff191682840152151560051b8201015f8080612670565b600954906126ee826125f0565b916126fc6040519384610719565b80835260095f90815260207f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af8186015b84841061273a575050505050565b60018381926127488561263f565b81520192019301929061272c565b634e487b7160e01b5f52603260045260245ffd5b60095481101561279f5760095f527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af01905f90565b612756565b5f929181546127b281612607565b9260019180831690811561280957506001146127cf575b50505050565b9091929394505f5260209060205f20905f915b8583106127f8575050505001905f8080806127c9565b8054858401529183019181016127e2565b60ff191684525050508115159091020191505f8080806127c9565b60209061283792604051938480936127a4565b9081520301902090565b634e487b7160e01b5f52601160045260245ffd5b5f1981146128635760010190565b612841565b90612872826125f0565b61287f6040519182610719565b8281528092612890601f19916125f0565b01905f5b8281106128a057505050565b806060602080938501015201612894565b805182101561279f5760209160051b010190565b5f546128d9906001600160a01b03166103e2565b60405163785f8ee360e01b81526001600160a01b038316600482015260209291908381602481855afa908115610516575f91612a31575b5015612a26576040516378a9bb4360e11b81526001600160a01b039290921660048301528290829060249082905afa90811561051657612960925f92610c9d5750505f52600260205260405f2090565b6009546004909101905f805b8281106129f2575061297d90612868565b915f905f5b838110612990575050505090565b6129a96108bb61058f846129a38561276a565b50612824565b6129b6575b600101612982565b916129ea6001916129cf6129c98661276a565b5061263f565b6129d982896128b1565b526129e481886128b1565b50612855565b9290506129ae565b612a056108bb61058f866129a38561276a565b612a12575b60010161296c565b90612a1e600191612855565b919050612a0a565b505050610b8a6126e1565b612a489150843d8611610552576105448183610719565b5f612910565b5f54612a62906001600160a01b03166103e2565b60405163785f8ee360e01b81526001600160a01b038316600482015260209391908481602481855afa908115610516575f91612b04575b5015612af8576040516378a9bb4360e11b81526001600160a01b039390931660048401528390839060249082905afa91821561051657610c93612af393600592610b8a965f92610c9d5750505f52600260205260405f2090565b61263f565b50505050610b8a611ab1565b612b1b9150853d8711610552576105448183610719565b5f612a99565b5f1981019190821161286357565b634e487b7160e01b5f525f60045260245ffd5b818110612b4d575050565b5f8155600101612b42565b9190601f8111612b6757505050565b61267f925f5260205f20906020601f840160051c83019310612b91575b601f0160051c0190612b42565b9091508190612b84565b919091828114612c8257612baf8354612607565b6001600160401b0381116106f957612bd181612bcb8454612607565b84612b58565b5f93601f8211600114612c1057612c0192939482915f92612c05575b50508160011b915f199060031b1c19161790565b9055565b015490505f80612bed565b612c22601f198316915f5260205f2090565b94612c30845f5260205f2090565b915f5b818110612c6a57509583600195969710612c52575b505050811b019055565b01545f1960f88460031b161c191690555f8080612c48565b87830154845560019384019390920191602001612c33565b509050565b91906114d55761267f91612b9b565b6009548015612d3c575f198101908082101561279f577f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7ae9060095f5201612cdd8154612607565b9081612ceb575b5050600955565b601f8211600114612d02575f9150555b5f80612ce4565b612d28612d37926001601f612d1a855f5260205f2090565b920160051c82019101612b42565b5f908082528160208120915555565b612cfb565b634e487b7160e01b5f52603160045260245ffd5b612d6890602060405192828480945193849201610a71565b810103902090565b5f54612d84906001600160a01b03166103e2565b60405163785f8ee360e01b81526001600160a01b038316600482015260209291908381602481855afa908115610516575f91612e4f575b5015612451576040516378a9bb4360e11b81526001600160a01b039290921660048301528290829060249082905afa90811561051657612e0b925f92610c9d5750505f52600260205260405f2090565b6009548015612e4857906004015f5b828110612e2957505050600190565b612e3c6108bb61058f846129a38561276a565b61245157600101612e1a565b5050600190565b612e669150843d8611610552576105448183610719565b5f612dbb565b91909182516001600160401b0381116106f957612e8d81612bcb8454612607565b602080601f8311600114612ec957508190612c019394955f92612ebe5750508160011b915f199060031b1c19161790565b015190505f80612bed565b90601f19831695612edd855f5260205f2090565b925f905b888210612f1757505083600195969710612eff57505050811b019055565b01515f1960f88460031b161c191690555f8080612c48565b80600185968294968601518155019501930190612ee1565b5f5460405163785f8ee360e01b81526001600160a01b039283166004820181905260209590949093909216908581602481855afa908115610516575f9161301c575b5015610cba5784602491604051928380926378a9bb4360e11b82528860048301525afa948515610516575f95612fea575b5050905f8051602061429383398151915291612fd6826109666004612fcf895f52600260205260405f2090565b0184611d5e565b612fe5604051928392836125d0565b0390a3565b5f8051602061429383398151915293929550908161301392903d1061050f576105008183610719565b9390915f612fa2565b6130339150863d8811610552576105448183610719565b5f612f71565b9190613043613c64565b5f5b8181106130525750505050565b5f54613066906001600160a01b03166103e2565b9061307a6130758285886131aa565b6131ba565b6040805163785f8ee360e01b81526001600160a01b039290921660048084019190915260209491929091908581602481855afa908115610516575f9161318d575b506130ce575b5050506001915001613045565b8461310d916130e1613075878a8d6131aa565b85516378a9bb4360e11b81526001600160a01b0390911685820190815290938492918391829160200190565b03915afa9182156105165761095f61313c9361049a936001985f92610c9d5750505f52600260205260405f2090565b61314a6130758386896131aa565b90517f06980050ce82d9f5a04e1ccb24535c222546e895d0d62b42154bd4241643cdfd3392858060a01b031691806131828982610b8d565b0390a35f80806130c1565b6131a49150863d8811610552576105448183610719565b5f6130bb565b919081101561279f5760051b0190565b35610b8a81610630565b5f549091906131db906001600160a01b03166103e2565b60405163785f8ee360e01b81526001600160a01b038416600482015260209391908481602481855afa908115610516575f91613275575b501561326d576040516378a9bb4360e11b81526001600160a01b039290921660048301528390829060249082905afa9283156105165761326993600692610c93925f92610c9d5750505f52600260205260405f2090565b5490565b505050505f90565b61328c9150853d8711610552576105448183610719565b5f613212565b5f54909291906132aa906001600160a01b03166103e2565b6040516378a9bb4360e11b81526001600160a01b03851660048201529190602090839060249082905afa918215610516577f6cf8224f35649bfbde77564cffb28be053bad6e412099ee796e956aff075fd5892613316915f916133b157505f52600260205260405f2090565b6133208154612855565b81556001600160a01b0382165f9081526007820160205260409020829085906133498154612855565b9055613372575b505060405192151583526001600160a01b039081169316918060208101612fe5565b600882600161339e94016133868154612855565b9055019060018060a01b03165f5260205260405f2090565b6133a88154612855565b90555f81613350565b61244b915060203d60201161050f576105008183610719565b5f549192916133e1906001600160a01b03166103e2565b60405163785f8ee360e01b81526001600160a01b038316600482015260209291908381602481855afa908115610516575f916134a5575b501561349a576040516378a9bb4360e11b81526001600160a01b039290921660048301529091908190839060249082905afa8015610516576132699261346e925f92610c9d5750505f52600260205260405f2090565b6001600160a01b03939093165f9081526007840160209081526040808320546008909601909152902090565b50505090505f905f90565b6134bc9150843d8611610552576105448183610719565b5f613418565b5f546040516378a9bb4360e11b815233600482015291929190602090829060249082906001600160a01b03165afa928315610516576005610c9361267f95613519945f916133b157505f52600260205260405f2090565b612e6c565b5f54613532906001600160a01b03166103e2565b6040516378a9bb4360e11b81526001600160a01b03831660048201529390602090859060249082905afa938415610516575f946135e9575b5061357483611d38565b546135bc575b5f80516020614293833981519152916135a482610966600461095f895f52600260205260405f2090565b612fe560405192839260018060a01b031695836125d0565b6135c582613934565b90506135d084611d38565b54111561357a576040516313fee0df60e21b8152600490fd5b61360391945060203d60201161050f576105008183610719565b925f61356a565b919290926136166138f0565b825115610e7f5761362d916108bb91858533613ad4565b61370c5761364661049a835f52600860205260405f2090565b5f5461365a906001600160a01b03166103e2565b6040516378a9bb4360e11b815233600482015290602090829060249082905afa80156105165761351960056136a385936136c3955f916133b157505f52600260205260405f2090565b016029906040519068191a5cd8dbdc99125960ba1b825260098201522090565b7f4bf5242860a031b49a24efb84dcf6766de2984a59b84b1210026e80b5a53121c604051806136f3339482610b8d565b0390a361267f60015f805160206142f383398151915255565b604051638baa579f60e01b8152600490fd5b5f546040516378a9bb4360e11b81526001600160a01b0392831660048201529160209183916024918391165afa9182156105165760ff92613775925f9161377a575b505f526002602052600460405f200190611d5e565b541690565b613793915060203d60201161050f576105008183610719565b5f613760565b6001600160a01b039081169291831561157857169182156115785761382d9261381b916137c46141c1565b6137cc6141c1565b6137d46141c1565b60015f805160206142f3833981519152556137ed6141c1565b6001600160601b0360a01b5f5416175f5560018060a01b03166001600160601b0360a01b6001541617600155565b60ff8019600554169115151617600555565b61383633613ce7565b5061384033613d92565b5061384a33613e4e565b5061385433613ebd565b5061385e33613f2c565b5061267f600a602e6040516d2b22aa22a920a72fa82620aca2a960911b81526006600e82015220556019602d6040516c22ac2822a92a2fa82620aca2a960991b81526006600d82015220556032602d6040516c26a0a9aa22a92fa82620aca2a960991b81526006600d82015220556014602c6040516b23aaa4a6222fa622a0a222a960a11b81526006600c8201522055565b5f805160206142f3833981519152600281541461390d5760029055565b604051633ee5aeb560e01b8152600490fd5b9081602091031261039d5751610b8a81610630565b60015490919061394c906001600160a01b03166103e2565b60405163bf40fac160e01b8152602060048201819052601260248301527122ac2822a924a2a721a2afa6a0a720a3a2a960711b604483015293918490829060649082905afa908115610516575f91613a6e575b506001600160a01b031690816139bb575b505090505f90600190565b6040516319522d9f60e01b81526001600160a01b0382166004820152918483602481845afa5f9381613a4f575b506139f357506139b0565b6040516334b735a160e01b81526001600160a01b039290921660048301528490829060249082905afa938415610516575f94613a30575b50509190565b613a47929450803d1061050f576105008183610719565b915f80613a2a565b613a67919450863d881161050f576105008183610719565b925f6139e8565b613a8e9150843d8611613a94575b613a868183610719565b81019061391f565b5f61399f565b503d613a7c565b60549492916001600160601b03199060601b168152613ac4825180936020601485019101610a71565b0191601483015260348201520190565b6007546001600160a01b039081169590949390929091908615613bb157844211613b9f57613b0d61058f845f52600860205260405f2090565b613b8e57613b29613b8995604051948593602085019586613a9b565b0391613b3d601f1993848101835282610719565b51902090613b816040519182610dd6602082019586603c917f19457468657265756d205369676e6564204d6573736167653a0a3332000000008252601c8201520190565b519020614075565b161490565b604051623f613760e71b8152600490fd5b604051630819bdcd60e01b8152600490fd5b604051630aeae73760e21b8152600490fd5b335f9081527fb16e88c42fd4e48df2dd6a2eabd6bc9aec654ec170056b470819f8892cc6431c602052604090205f805160206143138339815191529060ff905b541615611e745750565b335f9081527fab71e3f32666744d246edff3f96e4bdafee2e9867098cdd118a979a7464786a8602052604090207f189ab7a9244df0848122154315af71fe140f3db0fe014031783b0946b8c9d2e39060ff90613c03565b335f9081527f72cd09738b13b8f37d6f829e3529e6c68fe42286505d3d75fc989cd7172dfc0c602052604090207f8b98aa2c5e8e5cc63efb1164930fb01ca3b7750a60d94f08c39af078758aa67c9060ff90613c03565b5f8181525f805160206142d383398151915260209081526040808320338452909152902060ff90613c03565b6001600160a01b0381165f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d60205260409020545f805160206142d38339815191529060ff16612400575f808052602091825260408082206001600160a01b038516835290925220805460ff1916600117905533906001600160a01b03165f7f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b6001600160a01b0381165f9081527fb16e88c42fd4e48df2dd6a2eabd6bc9aec654ec170056b470819f8892cc6431c602052604090205f80516020614313833981519152905f805160206142d38339815191529060ff905b5416612451575f828152602091825260408082206001600160a01b038616835290925220805460ff1916600117905533916001600160a01b0316907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b6001600160a01b0381165f9081527fab71e3f32666744d246edff3f96e4bdafee2e9867098cdd118a979a7464786a8602052604090207f189ab7a9244df0848122154315af71fe140f3db0fe014031783b0946b8c9d2e3905f805160206142d38339815191529060ff90613dea565b6001600160a01b0381165f9081527fe0f087ff0e85a0e5564f4c99e8ede161bb357dfb3d6d4ae2c96fca5b90059fa3602052604090207fef17b1f3c5a412f9c8e887e3abd186802afec7dc657fd3b7002bf87b128dd0b1905f805160206142d38339815191529060ff90613dea565b6001600160a01b0381165f9081527f72cd09738b13b8f37d6f829e3529e6c68fe42286505d3d75fc989cd7172dfc0c602052604090207f8b98aa2c5e8e5cc63efb1164930fb01ca3b7750a60d94f08c39af078758aa67c905f805160206142d38339815191529060ff90613dea565b5f8181525f805160206142d3833981519152602081815260408084206001600160a01b038716855290915290912060ff90613dea565b5f8181525f805160206142d3833981519152602081815260408084206001600160a01b03871685529091529091205460ff1615612451575f828152602091825260408082206001600160a01b038616835290925220805460ff1916905533916001600160a01b0316907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b60ff601b9116019060ff821161286357565b90604181510361370c57602081015191606060408301519201515f1a90601b821061410c575b60ff8216601b8114159081614100575b5061370c576140de5f93602095604051948594859094939260ff6060936080840197845216602083015260408201520152565b838052039060015afa15610516575f51906001600160a01b0382161561370c57565b601c915014155f6140ab565b9061411690614063565b9061409b565b90813b156141a0575f805160206142b383398151915280546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a280511561418557614182916141ef565b50565b50503461418e57565b60405163b398979f60e01b8152600490fd5b604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b60ff5f805160206143338339815191525460401c16156141dd57565b604051631afcd79f60e31b8152600490fd5b5f80610b8a93602081519101845af43d1561422b573d9161420f8361073a565b9261421d6040519485610719565b83523d5f602085013e61422f565b6060915b90614256575080511561424457602081519101fd5b60405163d6bda27560e01b8152600490fd5b81511580614289575b614267575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b1561425f56fe8f3a324e5e9460b7562b6201535d730ee10ee7b35b28067e88cc44d5fc6f1402360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268009b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a2646970667358221220962b9943f339b47d30d5ffc06cbd929819c34fb066243b102d8a06244ddf30b864736f6c63430008180033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
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.