Overview
APE Balance
0 APE
APE Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
0x61016060 | 1711802 | 27 days ago | IN | 0 APE | 0.12589601 |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
Tournament
Compiler Version
v0.8.27+commit.40a35a09
Optimization Enabled:
Yes with 10 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {PVMath} from "../../libraries/PVMath.sol"; import {ITournament, ICredits, IBonusCash, IPriceFeed} from "./ITournament.sol"; import {IFeeSplitter} from "../../defi/feeSplitter/IFeeSplitter.sol"; import {ITickets} from "../../token/tickets/ITickets.sol"; import {ITokenRegistry} from "../../token/tokenRegistry/ITokenRegistry.sol"; import {IPlaythroughTracker} from "../../game/playthroughTracker/IPlaythroughTracker.sol"; import {IPlayerCard} from "../playerCard/IPlayerCard.sol"; import {ISwapExecutor} from "../../interfaces/ISwapExecutor.sol"; /** * @title Tournament * * @author Jack Chuma, Niftydude * * @notice Contract for managing tournaments. Handles tournament entries and result submission for payouts. */ contract Tournament is Initializable, AccessControlUpgradeable, ITournament { using PVMath for uint256; using Math for uint256; using SafeERC20 for IERC20; ITickets immutable TICKETS; ICredits immutable CREDITS; IPlayerCard immutable PLAYER_CARD; IPlaythroughTracker immutable PLAYTHROUGH_TRACKER; IFeeSplitter immutable FEE_SPLITTER; ITokenRegistry immutable TOKEN_REGISTRY; IBonusCash immutable BONUS_CASH; ISwapExecutor public swapExecutor; IPriceFeed public priceFeed; bytes32 constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); bytes32 constant RELAYER_ROLE = keccak256("RELAYER_ROLE"); // Tournament IDs uint256 public tournamentCount; // Game Dev address (receives excess collateral after tournaments end) address public gameDev; // Allow bonus cash to be used for rebuy bool public rebuyBonusCashAllowed; // time to wait for swap to confirm uint256 public swapDeadline; // dev fee in percent scaled to 1e18 uint256 public devFeePercentage; // Tournament ID => Config mapping(uint256 => Config) tournaments; // Tournament ID => Room ID => Room mapping(uint256 => mapping(uint256 => Room)) rooms; // Tournament ID => Player => EntryData mapping(uint256 => mapping(address => EntryData)) public players; // from token => to token => true if swap is enabled mapping(address => mapping(address => bool)) public swapEnabled; // credit id => total collateral held mapping(address => uint256) public totalCollateral; mapping(address => uint256) public emergencyCollateralBalance; mapping(address => uint256) public collateralResolverMax; constructor( ITickets _tickets, ICredits _credits, IPlayerCard _playerCard, IPlaythroughTracker _playthroughTracker, IBonusCash _bonusCash, IFeeSplitter _feeSplitter, ITokenRegistry _tokenRegistry ) { TICKETS = _tickets; CREDITS = _credits; PLAYER_CARD = _playerCard; PLAYTHROUGH_TRACKER = _playthroughTracker; BONUS_CASH = _bonusCash; FEE_SPLITTER = _feeSplitter; TOKEN_REGISTRY = _tokenRegistry; _disableInitializers(); } function __Tournament_init(address _superAdmin, address _gameDev, uint256 _swapDeadline) public initializer { _grantRole(DEFAULT_ADMIN_ROLE, _superAdmin); _grantRole(ADMIN_ROLE, _superAdmin); gameDev = _gameDev; swapDeadline = _swapDeadline; } /** * @notice Get the entry for a player in a room * * @param _tournamentId ID of the tournament * @param _player Address of the player * @param _roomId ID of the tournament room */ function getEntry(uint256 _tournamentId, address _player, uint256 _roomId) external view returns (Entry memory) { return players[_tournamentId][_player].entries[_roomId]; } /** * @notice Get the tournament config set by contract admin * * @param _tournamentId ID of the tournament */ function getTournament(uint256 _tournamentId) external view returns (Config memory) { return tournaments[_tournamentId]; } /** * @notice Get metadata for a tournament room * * @param _tournamentId ID of the tournament * @param _roomId ID of the tournament room */ function getRoom(uint256 _tournamentId, uint256 _roomId) external view returns (Room memory) { return rooms[_tournamentId][_roomId]; } /** * @notice Update the address of the game developer * * @param _gameDev Address of the game developer */ function setGameDev(address _gameDev) external onlyRole(ADMIN_ROLE) { require(_gameDev != address(0), "T: InvalidGameDev"); gameDev = _gameDev; emit GameDevSet(_gameDev); } /** * @notice Update the address of the swap executor * * @param _swapExecutor Address of the swap executor */ function setSwapExecutor(address _swapExecutor) external onlyRole(ADMIN_ROLE) { require(CREDITS.swapImplEnabled(_swapExecutor), "T: InvalidSwapExecutor"); swapExecutor = ISwapExecutor(_swapExecutor); emit SwapExecutorSet(_swapExecutor); } /** * @notice Update the address of the price feed * * @param _priceFeed Address of the price feed implementation */ function setPriceFeed(address _priceFeed) external onlyRole(ADMIN_ROLE) { priceFeed = IPriceFeed(_priceFeed); emit PriceFeedSet(_priceFeed); } /** * @notice Update the dev fee percentage * * @param _devFeePercentage dev fee percentage */ function setDevFeePercentage(uint256 _devFeePercentage) external onlyRole(ADMIN_ROLE) { require(_devFeePercentage <= FEE_SPLITTER.maxDevFeePercentage(), "T: IvalidDevFee"); devFeePercentage = _devFeePercentage; emit DevFeePercentageSet(_devFeePercentage); } /** * @notice Update the deadline for swap to confirm * * @param _swapDeadline time in seconds to wait for swap to confirm */ function setSwapDeadline(uint256 _swapDeadline) external onlyRole(ADMIN_ROLE) { swapDeadline = _swapDeadline; emit SwapDeadlineSet(_swapDeadline); } /** * @notice Update the treshold * * @param _creditId credit type to set max for * @param _collateralResolverMax time in seconds to wait for swap to confirm */ function setCollateralResolverMax(address _creditId, uint256 _collateralResolverMax) external onlyRole(ADMIN_ROLE) { collateralResolverMax[_creditId] = _collateralResolverMax; emit CollateralResolverMaxSet(_creditId, _collateralResolverMax); } /** * @notice Set whether bonus cash can be used for rebuy * * @param _allowed Whether bonus cash can be used for rebuy */ function setRebuyBonusCashAllowed(bool _allowed) external onlyRole(ADMIN_ROLE) { rebuyBonusCashAllowed = _allowed; emit RebuyBonusCashAllowedSet(_allowed); } /** * @notice Set whether swapping a credit type token pair is allowed * * @param _from token to swap from * @param _to token to swap to * @param _enabled true if swap is enabled */ function setSwapEnabled(address _from, address _to, bool _enabled) external onlyRole(ADMIN_ROLE) { swapEnabled[_from][_to] = _enabled; emit SwapEnabledChanged(_from, _to, _enabled); } /** * @notice Admin function to register a new tournament * * @param _c Config struct for the tournament */ function register(Config memory _c) external onlyRole(ADMIN_ROLE) returns (uint256 _tournamentId) { require(_c.endDate >= block.timestamp, "T: EndDateBeforeNow"); require(TOKEN_REGISTRY.isApproved(_c.creditId), "T: InvalidCreditId"); _validateTournamentConfig(_c); _tournamentId = _getNextTournamentId(); Config storage _config = tournaments[_tournamentId]; _config.entryFee = _c.entryFee; _config.rebuyFee = _c.rebuyFee; _config.creditId = _c.creditId; _config.creditRatio = uint96(CREDITS.tokenPerCreditRatio(_config.creditId)); _config.priceFeedPair = _c.priceFeedPair; _config.isExactFee = _c.isExactFee; IERC20(_config.creditId).forceApprove(address(CREDITS), type(uint256).max); IERC20(_config.creditId).forceApprove(address(TICKETS), type(uint256).max); _updateTournamentConfig(_config, _tournamentId, _c); } /** * @notice Admin function to update the config of a tournament * * @param _tournamentId ID of the tournament * @param _c Config struct for the tournament */ function updateTournamentConfig(uint256 _tournamentId, Config memory _c) external onlyRole(ADMIN_ROLE) { _validateTournamentId(_tournamentId); _validateTournamentConfig(_c); Config storage _config = tournaments[_tournamentId]; require(block.timestamp < _config.endDate, "T: TournamentOver"); _updateTournamentConfig(_config, _tournamentId, _c); } /** * @notice Opens new rooms for an active tournament * * @param _tournamentId ID of the tournament * @param _roomIds array containing IDs of the tournament rooms */ function createRoomBatch( uint256 _tournamentId, uint256[] calldata _roomIds, uint64[] calldata _openTimestamps ) external onlyRole(RELAYER_ROLE) { _validateTournamentId(_tournamentId); for (uint256 i; i < _roomIds.length; i++) { Config storage _config = tournaments[_tournamentId]; Room storage _room = rooms[_tournamentId][_roomIds[i]]; require(_room.startDate == 0, "T: Room already active"); _createRoom(_tournamentId, _roomIds[i], _openTimestamps[i], _config, _room); } } /** * @notice Opens a new room for an active tournament * * @param _tournamentId ID of the tournament * @param _roomId ID of the tournament room */ function createRoom(uint256 _tournamentId, uint256 _roomId, uint64 _openTimestamp) external onlyRole(RELAYER_ROLE) { _validateTournamentId(_tournamentId); Config storage _config = tournaments[_tournamentId]; Room storage _room = rooms[_tournamentId][_roomId]; require(_room.startDate == 0, "T: Room already active"); _createRoom(_tournamentId, _roomId, _openTimestamp, _config, _room); } /** * @notice Enters a player into a tournament room. If the room does not exist, it will be created. * * @dev A player can only enter a specific room once. * * @param _params entry params struct */ function enter(EntryParams calldata _params) external payable onlyRole(RELAYER_ROLE) { bool _isRebuy = _validateEntrance(_params.tournamentId, _params.roomId, _params.account, _params.entryAmount); bool _useBonusCash = _isRebuy ? rebuyBonusCashAllowed : true; Config memory _config = tournaments[_params.tournamentId]; uint256 _ticketBalance = TICKETS.balanceOf(_params.account, _config.creditId); uint256 _creditBalance = CREDITS.balanceOf(_params.account, _config.creditId); uint256 _creditsAndTicketsRequired = getCreditsRequired( _params.entryAmount, _config.priceFeedPair, _config.creditRatio ); uint256 _creditsAndTicketsRequiredAfterBonusCash = _useBonusCash ? BONUS_CASH.spendRequiredAmount( _params.account, _config.creditId, _ticketBalance, _creditBalance, _params.tournamentId, _creditsAndTicketsRequired ) : _creditsAndTicketsRequired; _creditBalance += _handleCreditSwapOrPurchaseIfNeeded( _params.account, _params.swapFromCredit, _params.swapMinCollateral, _config.creditId, _creditsAndTicketsRequiredAfterBonusCash, _creditBalance, _ticketBalance ); uint256 _collateral = _payFee( _params, _creditsAndTicketsRequired, _creditsAndTicketsRequiredAfterBonusCash, _creditBalance ); emit TournamentEntered(_params.tournamentId, _params.roomId, _params.account, _collateral, _isRebuy); } /** * @notice Allows any address with a balance and approval to add collateral to a given roomId * * @param _creditId ID of the tournament * @param _amount Amount of collateral to add */ function depositEmergencyCollateral(address _creditId, uint256 _amount) external { IERC20(_creditId).safeTransferFrom(msg.sender, address(this), _amount); totalCollateral[_creditId] += _amount; emergencyCollateralBalance[_creditId] += _amount; emit EmergencyCollateralDeposited(_creditId, _amount); } /** * @notice Submits the results of a tournament room. Payouts are calculated and credits are minted to players. * * @param _tournamentId ID of the tournament * @param _roomId ID of the tournament room * @param _results Array of results for the room */ function submitResults( uint256 _tournamentId, uint256 _roomId, Result[] memory _results ) external onlyRole(RELAYER_ROLE) { _validateTournamentId(_tournamentId); Config storage _config = tournaments[_tournamentId]; Room storage _room = rooms[_tournamentId][_roomId]; require(block.timestamp >= _room.endDate, "T: TournamentActive"); require(block.timestamp < _room.payoutEndDate, "T: PayoutDurationOver"); address[] memory _players = new address[](_results.length); uint256[] memory _ticketAmounts = new uint256[](_results.length); uint256[] memory _creditAmounts = new uint256[](_results.length); uint256 _totalPayout; for (uint256 i; i < _results.length; i++) { Result memory _result = _results[i]; _players[i] = _result.player; (_ticketAmounts[i], _creditAmounts[i]) = _processPayout(_tournamentId, _result, _roomId, _config); unchecked { _totalPayout += _ticketAmounts[i] + _creditAmounts[i]; } } _totalPayout *= _config.creditRatio; if (_room.totalPrizePool < _totalPayout) { _resolveUndercollateralization(_room, _config.creditId, _totalPayout); } _room.totalPrizePool -= _totalPayout; if (_room.collateral < _totalPayout) { _requestBonusCashCollateral(_room, _config, _totalPayout, _tournamentId); } else { _room.collateral -= _totalPayout; totalCollateral[_config.creditId] -= _totalPayout; } TICKETS.mintBatch(_config.creditId, _players, _ticketAmounts); CREDITS.mintBatch(_config.creditId, _players, _creditAmounts); emit ResultsSubmitted(_tournamentId, _roomId, _results); } /** * @notice Cleans up a tournament room after the payout duration has ended. Returns any remaining collateral to the game dev or bonus contract. * * @param _tournamentId ID of the tournament * @param _roomId ID of the tournament room */ function cleanup(uint256 _tournamentId, uint256 _roomId) external { _validateTournamentId(_tournamentId); Config storage _config = tournaments[_tournamentId]; Room storage _room = rooms[_tournamentId][_roomId]; require(_room.collateral > 0, "T: NoCollateralToReturn"); require(block.timestamp >= _room.payoutEndDate, "T: PayoutDurationNotOver"); address _target = gameDev; uint256 _amount = _room.collateral; uint256 _bonusCollateral = 0; _room.collateral = 0; totalCollateral[_config.creditId] -= _amount; emit TournamentCleansed(_tournamentId, _roomId, _target, _amount); if (_room.bonusCashCollected) { _target = address(BONUS_CASH); _bonusCollateral = _amount; } else { IERC20(_config.creditId).safeTransfer(gameDev, _amount); } if (_bonusCollateral > 0) { IERC20(_config.creditId).forceApprove(address(BONUS_CASH), _bonusCollateral); } BONUS_CASH.endTournament(_config.creditId, _tournamentId, _bonusCollateral); } /** * @notice Withdraw emergency collateral * * @param _creditId token address * @param _to token receiver address */ function withdrawEmergencyCollateral( address _creditId, address _to, uint256 _amount ) external onlyRole(ADMIN_ROLE) { require(emergencyCollateralBalance[_creditId] >= _amount, "T: InsufficientBalance"); emergencyCollateralBalance[_creditId] -= _amount; IERC20(_creditId).safeTransfer(_to, _amount); emit EmergencyCollateralWithdrawn(_creditId, _to, _amount); } /** * @notice Withdraw excess tokens not being used as collateral * * @param _token token address * @param _to token receiver address */ function withdrawExcess(address _token, address _to) external { require(TOKEN_REGISTRY.hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "OnlyGovernanceCanCall"); uint256 _excessAmount = IERC20(_token).balanceOf(address(this)) - totalCollateral[_token]; require(_excessAmount != 0, "C: NoExcess"); IERC20(_token).safeTransfer(_to, _excessAmount); emit ExcessTokensWithdrawn(_token, _to, _excessAmount); } /** * @notice Returns credits required to enter a tournament denominated in another collateral type * * @param _fee Collateral fee to convert to credits in wei * @param _tokenPair Price feed pair ID for conversion * @param _creditRatio Ratio of target currency to credits in wei */ function getCreditsRequired(uint256 _fee, bytes32 _tokenPair, uint256 _creditRatio) public view returns (uint256) { if (_tokenPair == bytes32(0)) return _fee; uint256 _price = priceFeed.getPrice(_tokenPair); uint256 _collateral = _fee.div(_price); uint256 _creditsRequired = uint256(1).max(_collateral.ceilDiv(_creditRatio)); return _creditsRequired; } /** * @notice Returns the split between credits, tickets and bonus cash for a user entering a specific tournament * * @param _account the user wallet * @param _creditId token address of the credit type to retrieve the split for * @param _entryFee the total entry fee for user and tournament */ function getEntrySplit( address _account, address _creditId, uint256 _entryFee ) external view returns (uint256 _amountCredits, uint256 _amountTickets, uint256 _amountBonusCash) { uint256 _ticketBalance = TICKETS.balanceOf(_account, _creditId); uint256 _creditBalance = CREDITS.balanceOf(_account, _creditId); _amountBonusCash = BONUS_CASH.getBonusCashForEntry( _account, _creditId, _ticketBalance, _creditBalance, _entryFee ); uint256 _entryFeeAfterBonusCash = _entryFee - _amountBonusCash; _amountCredits = _creditBalance.min(_entryFeeAfterBonusCash); _amountTickets = _entryFeeAfterBonusCash - _amountCredits; require(_ticketBalance >= _amountTickets, "T: InsufficientBalance"); } function _getNextTournamentId() private returns (uint256) { unchecked { return ++tournamentCount; } } function _validateTournamentId(uint256 _tournamentId) private view { require(_tournamentId <= tournamentCount && _tournamentId > 0, "T: InvalidTournamentId"); } function _validateTournamentConfig(Config memory _c) private view { require(_c.endDate > _c.startDate, "T: EndDateBeforeStartDate"); require(_c.entryLimit > 0, "T: EntryLimitZero"); require(_c.ticketProfitToTickets <= 1e18, "T: InvalidPercent"); require(_c.creditProfitToTickets <= 1e18, "T: InvalidPercent"); require(_c.creditEntryToTickets <= 1e18, "T: InvalidPercent"); if (_c.priceFeedPair != bytes32(0)) { require(priceFeed.getPrice(_c.priceFeedPair) > 0, "T: InvalidPriceFeedPair"); } } function _updateTournamentConfig(Config storage _config, uint256 _tournamentId, Config memory _c) private { _config.startDate = _c.startDate; _config.endDate = _c.endDate; _config.entryLimit = _c.entryLimit; _config.rebuyLimit = _c.rebuyLimit; _config.ticketProfitToTickets = _c.ticketProfitToTickets; _config.creditProfitToTickets = _c.creditProfitToTickets; _config.creditEntryToTickets = _c.creditEntryToTickets; _config.maxEntriesPerRoom = _c.maxEntriesPerRoom; _config.bonusCollateral = _c.bonusCollateral; _config.payoutDuration = _c.payoutDuration; _config.entryDuration = _c.entryDuration; _config.tournamentDuration = _c.tournamentDuration; emit TournamentConfigUpdated(_tournamentId, _c); } function _resolveUndercollateralization(Room storage _room, address _creditId, uint256 _totalPayout) private { uint256 _amountMissing = _totalPayout - _room.totalPrizePool; if ( _amountMissing <= collateralResolverMax[_creditId] && _amountMissing <= emergencyCollateralBalance[_creditId] ) { _room.totalPrizePool += _amountMissing; _room.collateral += _amountMissing; emergencyCollateralBalance[_creditId] -= _amountMissing; } else { revert("T: PrizePoolExceeded"); } } function _validateEntrance( uint256 _tournamentId, uint256 _roomId, address _account, uint256 _entryAmount ) private returns (bool _isRebuy) { _validateTournamentId(_tournamentId); Config storage _config = tournaments[_tournamentId]; Room storage _room = rooms[_tournamentId][_roomId]; Entry storage _entry = players[_tournamentId][_account].entries[_roomId]; EntryData storage _entryData = players[_tournamentId][_account]; if (_room.startDate == 0) { _createRoom(_tournamentId, _roomId, 0, _config, _room); } else { require(block.timestamp >= _room.startDate - _config.entryDuration, "T: RoomNotActive"); } uint256 _cutoffDate = _room.startDate; uint256 _configuredFee; _isRebuy = _entry.totalCollateralPaid > 0; if (_isRebuy) { _configuredFee = _config.rebuyFee; _cutoffDate = _room.endDate; require(_entry.rebuyCount < _config.rebuyLimit, "T: RebuyLimitReached"); unchecked { _entry.rebuyCount += 1; } } else { _configuredFee = _config.entryFee; require(_entryData.entryCount < _config.entryLimit, "T: PlayerEntryLimitReached"); require(_room.totalEntries < _config.maxEntriesPerRoom, "T: RoomEntryLimitReached"); unchecked { _entryData.entryCount += 1; _room.totalEntries += 1; } } require(block.timestamp < _cutoffDate, "T: TournamentStartedOrOver"); if (_config.isExactFee) { require(_entryAmount == _configuredFee, "T: InvalidEntryAmount"); } else { require(_entryAmount >= _configuredFee, "T: InvalidEntryAmount"); } } function _createRoom( uint256 _tournamentId, uint256 _roomId, uint64 _openTimestamp, Config storage _config, Room storage _room ) private { _openTimestamp = _openTimestamp != 0 ? _openTimestamp : uint64(block.timestamp); require(_openTimestamp >= _config.startDate && _openTimestamp < _config.endDate, "T: InvalidStartDate"); _room.startDate = _openTimestamp + _config.entryDuration; _room.endDate = _room.startDate + _config.tournamentDuration; _room.payoutEndDate = uint56(_room.endDate + _config.payoutDuration); emit RoomOpened(_tournamentId, _roomId); } function _handleCreditSwapOrPurchaseIfNeeded( address _account, address _swapFromCredit, uint256 _swapMaxCollateralIn, address _creditId, uint256 _fee, uint256 _creditBalance, uint256 _ticketBalance ) private returns (uint256 _amountCreditsPurchased) { if (msg.value > 0) { CREDITS.purchaseCredits{value: msg.value}(_fee, _account, _creditId, address(this)); _amountCreditsPurchased = _fee; } else if (_swapFromCredit != address(0)) { require(swapEnabled[_swapFromCredit][_creditId], "T: SwapNotEnabled"); CREDITS.swap( _swapFromCredit, _creditId, _account, _fee, block.timestamp + swapDeadline, _swapMaxCollateralIn, swapExecutor ); _amountCreditsPurchased = _fee; } else if (_ticketBalance + _creditBalance < _fee) { uint256 _ratio = CREDITS.tokenPerCreditRatio(_creditId); uint256 _amountNeeded = _fee - _ticketBalance - _creditBalance; uint256 _collateralAmount = _amountNeeded * _ratio; IERC20(_creditId).safeTransferFrom(_account, address(this), _collateralAmount); IERC20(_creditId).forceApprove(address(CREDITS), _collateralAmount); CREDITS.purchaseCredits(_amountNeeded, _account, _creditId, address(this)); _amountCreditsPurchased = _amountNeeded; } } function _payFee( EntryParams memory _params, uint256 _creditsAndTicketsRequired, uint256 _creditsAndTicketsRequiredAfterBonusCash, uint256 _creditBalance ) private returns (uint256 _collateral) { Config storage _config = tournaments[_params.tournamentId]; uint256 _creditsRequired = _creditBalance.min(_creditsAndTicketsRequiredAfterBonusCash); uint256 _ticketsRequired = _creditsAndTicketsRequiredAfterBonusCash - _creditsRequired; if (_creditsRequired > 0) { CREDITS.release(_params.account, _config.creditId, _creditsRequired); } if (_ticketsRequired > 0) { TICKETS.release(_params.account, address(this), _config.creditId, _ticketsRequired, false); } uint256 _totalCollateral = _config.creditRatio * _creditsAndTicketsRequired; uint256 _totalFee = _disperseProtocolFee(_params.account, _config, _totalCollateral, _params.tournamentId); //console.log(_totalFee); PLAYTHROUGH_TRACKER.progressAccount(_params.account, _config.creditId, msg.sender, _creditsAndTicketsRequired); return _updateRoomAndEntry( _params.tournamentId, _params.roomId, _params.account, _ticketsRequired * _config.creditRatio, _creditsRequired * _config.creditRatio, _totalCollateral, _totalFee, _config.creditId ); } function _updateRoomAndEntry( uint256 _tournamentId, uint256 _roomId, address _account, uint256 _ticketBasedCollateralAmount, uint256 _creditBasedCollateralAmount, uint256 _totalCollateral, uint256 _totalFee, address _creditId ) private returns (uint256 _collateral) { Room storage _room = rooms[_tournamentId][_roomId]; Entry storage _entry = players[_tournamentId][_account].entries[_roomId]; unchecked { _collateral = _totalCollateral - _totalFee; uint256 _totalBalance = _ticketBasedCollateralAmount + _creditBasedCollateralAmount; uint256 _newCollateral = _totalBalance - _totalBalance.min(_totalFee); _room.totalPrizePool += _collateral; _room.collateral += _newCollateral; _entry.totalCollateralPaid += _totalCollateral; _entry.ticketBasedCollateral += _ticketBasedCollateralAmount; _entry.creditBasedCollateral += _creditBasedCollateralAmount; totalCollateral[_creditId] += _newCollateral; } } function _disperseProtocolFee( address _account, Config storage _config, uint256 _totalCollateral, uint256 _tournamentId ) private returns (uint256) { uint256 _currentBalance = IERC20(_config.creditId).balanceOf(address(this)); (address[] memory _addrs, uint256[] memory _amounts, uint256 _totalFee) = FEE_SPLITTER.calculateFees( gameDev, _account, _totalCollateral, devFeePercentage ); if (_currentBalance < _totalFee) { BONUS_CASH.collateralPayout(_config.creditId, _tournamentId, _totalFee - _currentBalance); } for (uint256 i; i < _addrs.length; i++) { if (_addrs[i] != address(0) && _amounts[i] != 0) { IERC20(_config.creditId).safeTransfer(_addrs[i], _amounts[i]); } } return _totalFee; } function _processPayout( uint256 _tournamentId, Result memory _result, uint256 _roomId, Config storage _config ) private returns (uint256 _amountTickets, uint256 _amountCredits) { EntryData storage _entryData = players[_tournamentId][_result.player]; Entry storage _entry = _entryData.entries[_roomId]; require(_entry.ticketBasedCollateral > 0 || _entry.creditBasedCollateral > 0, "T: PlayerNotInRoom"); require(_entry.payoutReceived == false, "T: PayoutAlreadyReceived"); _entry.payoutReceived = true; (uint256 _ticketBasedMultiplier, uint256 _creditBasedMultiplier) = _calculatePayoutMultipliers( _result.multiplier, _entry, _config ); _amountTickets = _entry.totalCollateralPaid.mul(_ticketBasedMultiplier) / _config.creditRatio; _amountCredits = _entry.totalCollateralPaid.mul(_creditBasedMultiplier) / _config.creditRatio; } function _calculatePayoutMultipliers( uint256 _multiplier, Entry storage _entry, Config storage _c ) private view returns (uint256 _ticketBasedMultiplier, uint256 _creditBasedMultiplier) { uint256 _totalCollateralPaid = _entry.totalCollateralPaid; uint256 _percentTickets = _entry.ticketBasedCollateral.div(_totalCollateralPaid); uint256 _percentCredits = _entry.creditBasedCollateral.div(_totalCollateralPaid); if (_multiplier > 1e18) { // General case where there is a nonzero profit (_ticketBasedMultiplier, _creditBasedMultiplier) = _calculateMultipliersForProfit( _multiplier, _c, _entry.ticketBasedCollateral, _entry.creditBasedCollateral, _totalCollateralPaid, 1e18 - _percentTickets - _percentCredits ); } else if (_percentTickets + _percentCredits <= _multiplier) { // Case where all tickets/credits are recouped, but any bonus cash is not uint256 _converted = _entry.creditBasedCollateral.mul(_c.creditEntryToTickets).div(_totalCollateralPaid); uint256 _convDiff = 1e18 - _c.creditEntryToTickets; _ticketBasedMultiplier = _percentTickets + _converted; _creditBasedMultiplier = _entry.creditBasedCollateral.mul(_convDiff).div(_totalCollateralPaid); } else if (_percentTickets <= _multiplier) { // Case where some credits are not recouped uint256 _excess = _multiplier - _percentTickets; uint256 _creditsDiff = _excess.mul(_c.creditEntryToTickets); _ticketBasedMultiplier = _percentTickets + _creditsDiff; _creditBasedMultiplier = _multiplier - _ticketBasedMultiplier; } else { // Case where some tickets are not recouped _ticketBasedMultiplier = _multiplier; } } function _calculateMultipliersForProfit( uint256 _multiplier, Config memory _c, uint256 _ticketBasedCollateral, uint256 _creditBasedCollateral, uint256 _totalCollateralPaid, uint256 _percentBonus ) private pure returns (uint256 _ticketMultiplier, uint256 _creditMultiplier) { uint256 _profit = _multiplier - 1e18; uint256 _ticketProfitToTickets = _profit.mul(_c.ticketProfitToTickets); uint256 _creditProfitToTickets = _profit.mul(_c.creditProfitToTickets); uint256 _ticketsToTickets = _ticketBasedCollateral.mul(1e18 + _ticketProfitToTickets); uint256 _creditsToTickets = _creditBasedCollateral.mul(_c.creditEntryToTickets + _creditProfitToTickets); _ticketMultiplier = (_ticketsToTickets + _creditsToTickets).div(_totalCollateralPaid); _creditMultiplier = _multiplier - _ticketMultiplier - _percentBonus; } function _requestBonusCashCollateral( Room storage _room, Config storage _config, uint256 _collateralTotal, uint256 _tournamentId ) private { uint256 _amountNeeded = _config.bonusCollateral.max(_collateralTotal - _room.collateral); unchecked { uint256 _newCollateral = _amountNeeded - _collateralTotal; _room.collateral += _newCollateral; totalCollateral[_config.creditId] += _newCollateral; } _room.bonusCashCollected = true; BONUS_CASH.collateralPayout(_config.creditId, _tournamentId, _amountNeeded); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol) pragma solidity ^0.8.20; import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; import {ContextUpgradeable} from "../utils/ContextUpgradeable.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 { } /** * @dev See {IERC165-supportsInterface}. */ 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` to `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.0.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.20; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ```solidity * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Storage of the initializable contract. * * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions * when using with upgradeable contracts. * * @custom:storage-location erc7201:openzeppelin.storage.Initializable */ struct InitializableStorage { /** * @dev Indicates that the contract has been initialized. */ uint64 _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool _initializing; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00; /** * @dev The contract is already initialized. */ error InvalidInitialization(); /** * @dev The contract is not initializing. */ error NotInitializing(); /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint64 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in * production. * * Emits an {Initialized} event. */ modifier initializer() { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); // Cache values to avoid duplicated sloads bool isTopLevelCall = !$._initializing; uint64 initialized = $._initialized; // Allowed calls: // - initialSetup: the contract is not in the initializing state and no previous version was // initialized // - construction: the contract is initialized at version 1 (no reininitialization) and the // current contract is just being deployed bool initialSetup = initialized == 0 && isTopLevelCall; bool construction = initialized == 1 && address(this).code.length == 0; if (!initialSetup && !construction) { revert InvalidInitialization(); } $._initialized = 1; if (isTopLevelCall) { $._initializing = true; } _; if (isTopLevelCall) { $._initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint64 version) { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing || $._initialized >= version) { revert InvalidInitialization(); } $._initialized = version; $._initializing = true; _; $._initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { _checkInitializing(); _; } /** * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}. */ function _checkInitializing() internal view virtual { if (!_isInitializing()) { revert NotInitializing(); } } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing) { revert InvalidInitialization(); } if ($._initialized != type(uint64).max) { $._initialized = type(uint64).max; emit Initialized(type(uint64).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint64) { return _getInitializableStorage()._initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _getInitializableStorage()._initializing; } /** * @dev Returns a pointer to the storage namespace. */ // solhint-disable-next-line var-name-mixedcase function _getInitializableStorage() private pure returns (InitializableStorage storage $) { assembly { $.slot := INITIALIZABLE_STORAGE } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.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.0.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 ERC165 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 { } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol) pragma solidity ^0.8.20; /** * @dev External interface of AccessControl declared to support ERC165 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 signaling 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, an admin role * bearer except when using {AccessControl-_setupRole}. */ 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.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC20Permit} from "../extensions/IERC20Permit.sol"; import {Address} from "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev An operation with an ERC20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data); if (returndata.length != 0 && !abi.decode(returndata, (bool))) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error AddressInsufficientBalance(address account); /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedInnerCall(); /** * @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 AddressInsufficientBalance(address(this)); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert FailedInnerCall(); } } /** * @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 * {FailedInnerCall} 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 AddressInsufficientBalance(address(this)); } (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 {FailedInnerCall}) 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 {FailedInnerCall} 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 {FailedInnerCall}. */ 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 /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert FailedInnerCall(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * 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[EIP 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.0.0) (utils/math/Math.sol) pragma solidity ^0.8.20; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Muldiv operation overflow. */ error MathOverflowedMulDiv(); enum Rounding { Floor, // Toward negative infinity Ceil, // Toward positive infinity Trunc, // Toward zero Expand // Away from zero } /** * @dev Returns the addition of two unsigned integers, with an overflow flag. */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the subtraction of two unsigned integers, with an overflow flag. */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds towards infinity instead * of rounding towards zero. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { if (b == 0) { // Guarantee the same behavior as in a regular Solidity division. return a / b; } // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or * denominator == 0. * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by * Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0 = x * y; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. if (denominator <= prod1) { revert MathOverflowedMulDiv(); } /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. // Always >= 1. See https://cs.stackexchange.com/q/138556/92363. uint256 twos = denominator & (0 - denominator); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also // works in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded * towards zero. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256 of a positive value rounded towards zero. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0); } } /** * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers. */ function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) { return uint8(rounding) % 2 == 1; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; interface IAffiliateRegistry is IAccessControl { struct Affiliate { uint256 lifetimeSales; uint256 lifetimeSessionValue; address receiver; uint32 sessionShareTier; uint32 saleShareTier; uint64 uniqueWallets; } error AffiliateRegistry__AlreadyRegistered(); error AffiliateRegistry__NoSelfAffiliate(); error AffiliateRegistry__AffiliateNotRegistered(); error AffiliateRegistry__ZeroValue(); error AffiliateRegistry__NoPermission(); error AffiliateRegistry__ZeroAddress(); event AffiliateRegistered(uint256 indexed id, address indexed receiver); event ReceiverChanged(uint256 indexed id, address indexed receiver); event PlayerAssigned(uint256 indexed id, address indexed player); event SessionShareChanged(uint256 indexed id, uint256 newSessionShare); event SaleShareChanged(uint256 indexed id, uint256 newSaleShare); event SaleShareTierChanged(uint32 tier, uint256 saleShare); event SessionShareTierChanged(uint32 tier, uint256 sessionShare); event SaleRegistered(uint256 affiliateId, uint256 totalPrice, address receiver, uint256 amount); event SessionRegistered(uint256 affiliateId, uint256 totalPrice, address receiver, uint256 amount); function assignPlayer(address _player, uint256 _affiliateId) external; function setAffiliateSessionShareTier(uint256, uint32) external; function setAffiliateSaleShareTier(uint256, uint32) external; function registerNewAffiliate(address _revenueReceiver) external returns (uint256 _affiliateId); function registerSale(address _player, uint256 _fees) external returns (address, uint256); function registerSession(address _player, uint256 _fees) external returns (address, uint256); function changeReceiver(uint256 _affiliateId, address _newReceiver) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; import {IAffiliateRegistry} from "../../affiliateRegistry/IAffiliateRegistry.sol"; interface IFeeSplitter { event MaxDevFeePercentageSet(uint256 maxDevFeePercentage); event ProtocolFeePercentageSet(uint256 protocolFeePercentage); event ProtocolFeeReceiverSet(address protocolFeeReceiver); function maxDevFeePercentage() external view returns (uint256); function calculateFees( address _gameDev, address _player, uint256 _totalCollateral, uint256 _gameDevFeePercentage ) external returns (address[] memory, uint256[] memory, uint256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; interface IPlayerCard { error PlayerCard__SoulboundToken(); event BaseUriSet(string uri); function mintCardIfRequired(address to) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; interface IPlaythroughTracker { struct State { uint256 total; mapping(uint256 offeringId => uint256) totalPerOffering; } event RequirementAdded(address account, address creditId, uint256 offeringId, uint256 amount); event AccountProgressed(address account, address creditId, uint256 amount); event RequirementsAdded(address[] accounts, uint256[] amounts, address creditId, uint256 offeringId); event BonusCashSet(address bonusCash); function isLocked(address _account, address _creditId) external view returns (bool); function addRequirements( address[] calldata _accounts, uint256[] calldata _amounts, address _creditId, uint256 _offeringId ) external; function progressAccount(address _account, address _creditId, address _gameAddress, uint256 _amount) external; function progressAccount(uint256 _offeringId, address _creditId, address _account, uint256 _amount) external; function progressAccounts( uint256 _offeringId, address _creditId, address[] calldata _accounts, uint256[] memory _amounts ) external; function undoOffering(uint256 _offeringId, address _creditId, address _account) external; function undoOfferings(uint256 _offeringId, address _creditId, address[] calldata _accounts) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; import {ICredits} from "../../token/credits/ICredits.sol"; import {IBonusCash} from "../../token/bonusCash/IBonusCash.sol"; import {IPriceFeed} from "../../utils/priceFeed/IPriceFeed.sol"; import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; interface ITournament is IAccessControl { struct Config { uint256 entryFee; // credit fee to enter tournament (new entry for a new score) uint256 rebuyFee; // credit fee to rebuy into tournament (re-attempt an existing entry to try to improve score) bool isExactFee; // if true, above fees are exact required fees. if false, above fees are minimum fees uint64 entryLimit; // max number of entries allowed per player uint64 maxEntriesPerRoom; // max number of entries allowed per room uint64 rebuyLimit; // max number of rebuys allowed per player per entry uint64 startDate; // start date of tournament (block timestamp) uint64 endDate; // end date of tournament (block timestamp) uint64 ticketProfitToTickets; // % of profit from ticket entry converted to ticket payout (in wei) uint64 creditProfitToTickets; // % of profit from credit entry converted to credit payout (in wei) uint64 creditEntryToTickets; // % of credit entry converted to ticket payout (in wei) uint64 entryDuration; // duration in seconds of entry period for a room once the room is opened uint64 tournamentDuration; // duration in seconds of tournament instance (room) once started uint64 payoutDuration; // duration in seconds to allow payouts for after tournament ends uint96 creditRatio; // ratio of collateral to credits (gets set during register transaction) address creditId; // credit id to use for entry fee (the id is the collateral token address) uint256 bonusCollateral; // amount of collateral to request each time it's needed bytes32 priceFeedPair; // pair to use for price feed (use bytes32(0) for a flat credit fee) } struct Entry { uint256 ticketBasedCollateral; uint256 creditBasedCollateral; uint256 totalCollateralPaid; uint248 rebuyCount; bool payoutReceived; } struct EntryParams { address account; uint256 tournamentId; uint256 roomId; address swapFromCredit; uint256 swapMinCollateral; uint256 entryAmount; } struct EntryData { uint256 entryCount; // roomId => entry mapping(uint256 => Entry) entries; } struct Room { uint256 totalPrizePool; uint256 collateral; uint64 totalEntries; uint64 startDate; // block.timestamp + config.entryDuration uint64 endDate; // startDate + config.tournamentDuration uint56 payoutEndDate; // endDate + config.payoutDuration bool bonusCashCollected; } struct Result { address player; uint256 multiplier; } event TournamentConfigUpdated(uint256 indexed tournamentId, Config config); event TournamentEntered( uint256 indexed tournamentId, uint256 indexed roomId, address indexed player, uint registrationFee, bool isRebuy ); event ResultsSubmitted(uint256 tournamentId, uint256 _roomId, Result[] results); event GameDevSet(address gameDev); event BonusContractSet(address bonusContract); event SwapExecutorSet(address swapExecutor); event SwapDeadlineSet(uint256 deadline); event FeeSplitterSet(address feeSplitter); event TournamentCleansed(uint256 tournamentId, uint256 roomId, address target, uint256 amount); event RebuyBonusCashAllowedSet(bool allowed); event SwapEnabledChanged(address indexed from, address indexed to, bool enabled); event PriceFeedSet(address priceFeed); event RoomOpened(uint256 indexed tournamentId, uint256 indexed roomId); event ExcessTokensWithdrawn(address token, address to, uint256 excessAmount); event EmergencyCollateralWithdrawn(address token, address to, uint256 amount); event DevFeePercentageSet(uint256 percentage); event CollateralResolverMaxSet(address indexed creditId, uint256 collateralResolverMax); event EmergencyCollateralDeposited(address indexed creditId, uint256 amount); // Tournament IDs function tournamentCount() external view returns (uint256); // Game Dev address (receives excess collateral after tournaments end) function gameDev() external view returns (address); // Allow bonus cash to be used for rebuy function rebuyBonusCashAllowed() external view returns (bool); /** * @notice Get the tournament config set by contract admin * * @param _tournamentId ID of the tournament */ function getTournament(uint256 _tournamentId) external view returns (Config memory); /** * @notice Get the entry for a player in a room * * @param _tournamentId ID of the tournament * @param _player Address of the player * @param _roomId ID of the tournament room */ function getEntry(uint256 _tournamentId, address _player, uint256 _roomId) external view returns (Entry memory); /** * @notice Get metadata for a tournament room * * @param _tournamentId ID of the tournament * @param _roomId ID of the tournament room */ function getRoom(uint256 _tournamentId, uint256 _roomId) external view returns (Room memory); /** * @notice Update the address of the game developer * * @param _gameDev Address of the game developer */ function setGameDev(address _gameDev) external; /** * @notice Set whether bonus cash can be used for rebuy * * @param _allowed Whether bonus cash can be used for rebuy */ function setRebuyBonusCashAllowed(bool _allowed) external; /** * @notice Admin function to register a new tournament * * @param _c Config struct for the tournament */ function register(Config memory _c) external returns (uint256 _tournamentId); /** * @notice Admin function to update the config of a tournament * * @param _tournamentId ID of the tournament * @param _c Config struct for the tournament */ function updateTournamentConfig(uint256 _tournamentId, Config memory _c) external; /** * @notice Enters a player into a tournament room. If the room does not exist, it will be created. * * @dev A player can only enter a specific room once. * * @param _params entry params struct */ function enter(EntryParams calldata _params) external payable; /** * @notice Submits the results of a tournament room. Payouts are calculated and credits are minted to players. * * @param _tournamentId ID of the tournament * @param _roomId ID of the tournament room * @param _results Array of results for the room */ function submitResults(uint256 _tournamentId, uint256 _roomId, Result[] memory _results) external; /** * @notice Cleans up a tournament room after the payout duration has ended. Returns any remaining collateral to the game dev or bonus contract. * * @param _tournamentId ID of the tournament * @param _roomId ID of the tournament room */ function cleanup(uint256 _tournamentId, uint256 _roomId) external; function createRoomBatch( uint256 _tournamentId, uint256[] calldata _roomIds, uint64[] calldata _openTimestamps ) external; function createRoom(uint256 _tournamentId, uint256 _roomId, uint64 _openTimestamp) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; interface ISwapExecutor { error ZeroAddress(); event FeeSet(uint24 fee); function executeSwap( address _fromToken, address _toToken, uint256 _amount, uint256 _deadline, uint256 _amountOutMinimum ) external returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; library PVMath { function mul(uint256 a, uint256 b) internal pure returns (uint256) { return (a * b) / 1e18; } function div(uint256 a, uint256 b) internal pure returns (uint256) { return (a * 1e18) / b; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; import {ICredits} from "../credits/ICredits.sol"; interface IBonusCash { struct BonusCashInfo { uint256 totalSupply; address creditId; uint256 creditRatio; uint256 collateral; uint256 targetEntryPct; uint256 activeCount; address admin; bool invalidated; // gameAddress => isIncluded mapping(address => bool) included; // playerAddress => startingBalance mapping(address => uint256) startingBalance; // gameAddress => tournamentId => hasActiveTournament mapping(address => mapping(uint256 => bool)) isActive; // gameAddress => tournamentId => pendingBonusCashAmount mapping(address => mapping(uint256 => uint256)) pendingSupply; } struct Distribution { uint256 offeringId; address[] players; uint256[] amounts; uint256[] playthroughRequirements; bytes[] sigs; } event MinCollateralRatioSet(uint256 minCollateralRatio); event OfferingUpdated( uint256 offeringId, address creditId, uint256 collateral, uint256 targetEntryPct, address[] gamesAdded, address[] gamesRemoved, address admin ); event PlaythroughUndone(uint256 offeringId, address[] players); event BonusCashDistributed(uint256 offeringId, address[] players, uint256[] amounts); event BonusCashSpent(uint256 offeringId, uint256 tournamentId, address game, address player, uint256 amount); event CollateralSent(uint256 offeringId, uint256 tournamentId, uint256 amount, address game); event TournamentEnded(address game, uint256 tournamentId); event OfferingInvalidated(uint256 offeringId); event CollateralWithdrawn(uint256 offeringId, address to, uint256 amount); event CreditsSet(ICredits credits); event PlaythroughTrackerSet(address playthroughTracker); event MaxAllowedPlaythroughSet(uint256 playthrough); event PlayerOptedOut(address player, uint256 offeringId); function creditToGameToOfferingId(address creditId, address game) external view returns (uint256); function gameBalanceOf(address game, address creditId, address player) external view returns (uint256); function gameTargetEntryPct(address game, address creditId) external view returns (uint256); function spendRequiredAmount( address _account, address _token, uint256 _amountTickets, uint256 _amountCredits, uint256 _tournamentId, uint256 _fee ) external returns (uint256); function getBonusCashForEntry( address _account, address _token, uint256 _ticketBalance, uint256 _creditBalance, uint256 _entryFee ) external view returns (uint256 _requiredAmount); function collateralPayout(address _creditId, uint256 _tournamentId, uint256 _amount) external; function endTournament(address _creditId, uint256 _tournamentId, uint256 _collateralToReturn) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; import {ISemiFungibleSoulboundTokenUpgradeable} from "../sfst/ISemiFungibleSoulboundTokenUpgradeable.sol"; import {ISwapExecutor} from "../../interfaces/ISwapExecutor.sol"; interface ICredits is IAccessControl, ISemiFungibleSoulboundTokenUpgradeable { error Credits__ZeroAddress(); event WapeSet(address wape); event PlaythroughTrackerSet(address playthroughTracker); event AllowanceWithdrawal(address token, address receiver, uint256 amount); event CreditsPurchased(address token, address payFrom, address mintTo, uint256 amount); event CreditTypeConfigured(address token, uint256 ratio); event BonusCashSet(address bonusCash); event TicketsSet(address tickets); event CreditsReleased(address _from, address _to, address _token, uint256 _amount); event FeeReleased( address account, address token, uint256 amount, uint256 tournamentId, uint256 collateralAmount, uint256 amountLiquid, uint256 amountIlliquid ); event ExcessTokensWithdrawn(address token, address to, uint256 excessAmount); event CreditsSwapped(address fromToken, address toToken, uint256 tokensIn, uint256 creditsOut); event SwapImplEnabledChanged(address indexed swapImpl, bool enabled); function release(address _from, address _token, uint256 _amount) external returns (uint256 _releasedAmount); function purchaseCredits(uint256 _amount, address _mintTo, address _token, address _payFrom) external payable; function mintBatch(address _creditId, address[] memory _players, uint256[] memory _amounts) external; function tokenPerCreditRatio(address _creditId) external view returns (uint256); function purchaseCreditsWithGivenCollateral( uint256 _collateralAmount, address _mintTo, address _tokenAddress, address _payFrom ) external payable returns (uint256); function swap( address _fromToken, address _toToken, address _account, uint256 _creditsRequired, uint256 _deadline, uint256 _maxAmountTokensIn, ISwapExecutor _swapImpl ) external; function swapImplEnabled(address _swapImpl) external view returns (bool); function balanceOf(address account, address token) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; interface ISemiFungibleSoulboundTokenUpgradeable is IERC165 { /** * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. * @param balance Current balance for the interacting account. * @param needed Minimum amount required to perform a transfer. * @param tokenId Identifier number of a token. */ error InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error InvalidReceiver(address receiver); /** * @dev Indicates a failure with the token `value`. Used in transfers. * @param value value of tokens to transfer. */ error InvalidValue(uint256 value); /** * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation. * Used in batch transfers. * @param idsLength Length of the array of token identifiers * @param valuesLength Length of the array of token amounts */ error InvalidArrayLength(uint256 idsLength, uint256 valuesLength); /** * @dev Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`. */ event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value); /** * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all * transfers. */ event TransferBatch( address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values ); /** * @dev Returns the value of tokens of token type `id` owned by `account`. * * Requirements: * * - `account` cannot be the zero address. */ function balanceOf(address account, uint256 id) external view returns (uint256); /** * @dev Batched version of {balanceOf}. * * Requirements: * * - `accounts` and `ids` must have the same length. */ function balanceOfBatch( address[] calldata accounts, uint256[] calldata ids ) external view returns (uint256[] memory); /** * @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`. * * Emits a {TransferSingle} event. * * Requirements: * * - `to` cannot be the zero address. * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}. * - `from` must have a balance of tokens of type `id` of at least `value` amount. */ function transferFrom(address from, address to, uint256 id, uint256 value) external; /** * Emits either a {TransferSingle} or a {TransferBatch} event, depending on the length of the array arguments. * * Requirements: * * - `ids` and `values` must have the same length. */ function batchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata values) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; import {ISemiFungibleSoulboundTokenUpgradeable} from "../sfst/ISemiFungibleSoulboundTokenUpgradeable.sol"; interface ITickets is ISemiFungibleSoulboundTokenUpgradeable { event TicketTypeConfigured(address token, uint256 ratio); event WapeSet(address wape); event PlaythroughTrackerSet(address playthroughTracker); event ExcessTokensWithdrawn(address token, address to, uint256 excessAmount); function tokensPerTicket(address token) external view returns (uint256); function mintBatch(address _token, address[] memory _players, uint256[] memory _amounts) external; function release( address _from, address _to, address _token, uint256 _amount, bool _nativeApe ) external returns (uint256 _releasedAmount); function configureTicketType(address _tokenContract, uint256 _tokenPerCreditRatio) external; function balanceOf(address account, address token) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; interface ITokenRegistry is IAccessControl { event TokenUpdated(address token, bool approved); function isApproved(address _token) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; interface IPriceFeed { function getPrice(bytes32 _priceFeedId) external view returns (uint256); }
{ "viaIR": false, "optimizer": { "enabled": true, "runs": 10 }, "evmVersion": "paris", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"contract ITickets","name":"_tickets","type":"address"},{"internalType":"contract ICredits","name":"_credits","type":"address"},{"internalType":"contract IPlayerCard","name":"_playerCard","type":"address"},{"internalType":"contract IPlaythroughTracker","name":"_playthroughTracker","type":"address"},{"internalType":"contract IBonusCash","name":"_bonusCash","type":"address"},{"internalType":"contract IFeeSplitter","name":"_feeSplitter","type":"address"},{"internalType":"contract ITokenRegistry","name":"_tokenRegistry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"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":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"bonusContract","type":"address"}],"name":"BonusContractSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creditId","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateralResolverMax","type":"uint256"}],"name":"CollateralResolverMaxSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"percentage","type":"uint256"}],"name":"DevFeePercentageSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creditId","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EmergencyCollateralDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EmergencyCollateralWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"excessAmount","type":"uint256"}],"name":"ExcessTokensWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"feeSplitter","type":"address"}],"name":"FeeSplitterSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"gameDev","type":"address"}],"name":"GameDevSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"priceFeed","type":"address"}],"name":"PriceFeedSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"allowed","type":"bool"}],"name":"RebuyBonusCashAllowedSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tournamentId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_roomId","type":"uint256"},{"components":[{"internalType":"address","name":"player","type":"address"},{"internalType":"uint256","name":"multiplier","type":"uint256"}],"indexed":false,"internalType":"struct ITournament.Result[]","name":"results","type":"tuple[]"}],"name":"ResultsSubmitted","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":"uint256","name":"tournamentId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"roomId","type":"uint256"}],"name":"RoomOpened","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"SwapDeadlineSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"SwapEnabledChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"swapExecutor","type":"address"}],"name":"SwapExecutorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tournamentId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"roomId","type":"uint256"},{"indexed":false,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TournamentCleansed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tournamentId","type":"uint256"},{"components":[{"internalType":"uint256","name":"entryFee","type":"uint256"},{"internalType":"uint256","name":"rebuyFee","type":"uint256"},{"internalType":"bool","name":"isExactFee","type":"bool"},{"internalType":"uint64","name":"entryLimit","type":"uint64"},{"internalType":"uint64","name":"maxEntriesPerRoom","type":"uint64"},{"internalType":"uint64","name":"rebuyLimit","type":"uint64"},{"internalType":"uint64","name":"startDate","type":"uint64"},{"internalType":"uint64","name":"endDate","type":"uint64"},{"internalType":"uint64","name":"ticketProfitToTickets","type":"uint64"},{"internalType":"uint64","name":"creditProfitToTickets","type":"uint64"},{"internalType":"uint64","name":"creditEntryToTickets","type":"uint64"},{"internalType":"uint64","name":"entryDuration","type":"uint64"},{"internalType":"uint64","name":"tournamentDuration","type":"uint64"},{"internalType":"uint64","name":"payoutDuration","type":"uint64"},{"internalType":"uint96","name":"creditRatio","type":"uint96"},{"internalType":"address","name":"creditId","type":"address"},{"internalType":"uint256","name":"bonusCollateral","type":"uint256"},{"internalType":"bytes32","name":"priceFeedPair","type":"bytes32"}],"indexed":false,"internalType":"struct ITournament.Config","name":"config","type":"tuple"}],"name":"TournamentConfigUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tournamentId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"roomId","type":"uint256"},{"indexed":true,"internalType":"address","name":"player","type":"address"},{"indexed":false,"internalType":"uint256","name":"registrationFee","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isRebuy","type":"bool"}],"name":"TournamentEntered","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_superAdmin","type":"address"},{"internalType":"address","name":"_gameDev","type":"address"},{"internalType":"uint256","name":"_swapDeadline","type":"uint256"}],"name":"__Tournament_init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tournamentId","type":"uint256"},{"internalType":"uint256","name":"_roomId","type":"uint256"}],"name":"cleanup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"collateralResolverMax","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tournamentId","type":"uint256"},{"internalType":"uint256","name":"_roomId","type":"uint256"},{"internalType":"uint64","name":"_openTimestamp","type":"uint64"}],"name":"createRoom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tournamentId","type":"uint256"},{"internalType":"uint256[]","name":"_roomIds","type":"uint256[]"},{"internalType":"uint64[]","name":"_openTimestamps","type":"uint64[]"}],"name":"createRoomBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_creditId","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"depositEmergencyCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"devFeePercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"emergencyCollateralBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"tournamentId","type":"uint256"},{"internalType":"uint256","name":"roomId","type":"uint256"},{"internalType":"address","name":"swapFromCredit","type":"address"},{"internalType":"uint256","name":"swapMinCollateral","type":"uint256"},{"internalType":"uint256","name":"entryAmount","type":"uint256"}],"internalType":"struct ITournament.EntryParams","name":"_params","type":"tuple"}],"name":"enter","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"gameDev","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"bytes32","name":"_tokenPair","type":"bytes32"},{"internalType":"uint256","name":"_creditRatio","type":"uint256"}],"name":"getCreditsRequired","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tournamentId","type":"uint256"},{"internalType":"address","name":"_player","type":"address"},{"internalType":"uint256","name":"_roomId","type":"uint256"}],"name":"getEntry","outputs":[{"components":[{"internalType":"uint256","name":"ticketBasedCollateral","type":"uint256"},{"internalType":"uint256","name":"creditBasedCollateral","type":"uint256"},{"internalType":"uint256","name":"totalCollateralPaid","type":"uint256"},{"internalType":"uint248","name":"rebuyCount","type":"uint248"},{"internalType":"bool","name":"payoutReceived","type":"bool"}],"internalType":"struct ITournament.Entry","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_creditId","type":"address"},{"internalType":"uint256","name":"_entryFee","type":"uint256"}],"name":"getEntrySplit","outputs":[{"internalType":"uint256","name":"_amountCredits","type":"uint256"},{"internalType":"uint256","name":"_amountTickets","type":"uint256"},{"internalType":"uint256","name":"_amountBonusCash","type":"uint256"}],"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":"uint256","name":"_tournamentId","type":"uint256"},{"internalType":"uint256","name":"_roomId","type":"uint256"}],"name":"getRoom","outputs":[{"components":[{"internalType":"uint256","name":"totalPrizePool","type":"uint256"},{"internalType":"uint256","name":"collateral","type":"uint256"},{"internalType":"uint64","name":"totalEntries","type":"uint64"},{"internalType":"uint64","name":"startDate","type":"uint64"},{"internalType":"uint64","name":"endDate","type":"uint64"},{"internalType":"uint56","name":"payoutEndDate","type":"uint56"},{"internalType":"bool","name":"bonusCashCollected","type":"bool"}],"internalType":"struct ITournament.Room","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tournamentId","type":"uint256"}],"name":"getTournament","outputs":[{"components":[{"internalType":"uint256","name":"entryFee","type":"uint256"},{"internalType":"uint256","name":"rebuyFee","type":"uint256"},{"internalType":"bool","name":"isExactFee","type":"bool"},{"internalType":"uint64","name":"entryLimit","type":"uint64"},{"internalType":"uint64","name":"maxEntriesPerRoom","type":"uint64"},{"internalType":"uint64","name":"rebuyLimit","type":"uint64"},{"internalType":"uint64","name":"startDate","type":"uint64"},{"internalType":"uint64","name":"endDate","type":"uint64"},{"internalType":"uint64","name":"ticketProfitToTickets","type":"uint64"},{"internalType":"uint64","name":"creditProfitToTickets","type":"uint64"},{"internalType":"uint64","name":"creditEntryToTickets","type":"uint64"},{"internalType":"uint64","name":"entryDuration","type":"uint64"},{"internalType":"uint64","name":"tournamentDuration","type":"uint64"},{"internalType":"uint64","name":"payoutDuration","type":"uint64"},{"internalType":"uint96","name":"creditRatio","type":"uint96"},{"internalType":"address","name":"creditId","type":"address"},{"internalType":"uint256","name":"bonusCollateral","type":"uint256"},{"internalType":"bytes32","name":"priceFeedPair","type":"bytes32"}],"internalType":"struct ITournament.Config","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","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":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"players","outputs":[{"internalType":"uint256","name":"entryCount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceFeed","outputs":[{"internalType":"contract IPriceFeed","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rebuyBonusCashAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"entryFee","type":"uint256"},{"internalType":"uint256","name":"rebuyFee","type":"uint256"},{"internalType":"bool","name":"isExactFee","type":"bool"},{"internalType":"uint64","name":"entryLimit","type":"uint64"},{"internalType":"uint64","name":"maxEntriesPerRoom","type":"uint64"},{"internalType":"uint64","name":"rebuyLimit","type":"uint64"},{"internalType":"uint64","name":"startDate","type":"uint64"},{"internalType":"uint64","name":"endDate","type":"uint64"},{"internalType":"uint64","name":"ticketProfitToTickets","type":"uint64"},{"internalType":"uint64","name":"creditProfitToTickets","type":"uint64"},{"internalType":"uint64","name":"creditEntryToTickets","type":"uint64"},{"internalType":"uint64","name":"entryDuration","type":"uint64"},{"internalType":"uint64","name":"tournamentDuration","type":"uint64"},{"internalType":"uint64","name":"payoutDuration","type":"uint64"},{"internalType":"uint96","name":"creditRatio","type":"uint96"},{"internalType":"address","name":"creditId","type":"address"},{"internalType":"uint256","name":"bonusCollateral","type":"uint256"},{"internalType":"bytes32","name":"priceFeedPair","type":"bytes32"}],"internalType":"struct ITournament.Config","name":"_c","type":"tuple"}],"name":"register","outputs":[{"internalType":"uint256","name":"_tournamentId","type":"uint256"}],"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":"_creditId","type":"address"},{"internalType":"uint256","name":"_collateralResolverMax","type":"uint256"}],"name":"setCollateralResolverMax","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_devFeePercentage","type":"uint256"}],"name":"setDevFeePercentage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_gameDev","type":"address"}],"name":"setGameDev","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_priceFeed","type":"address"}],"name":"setPriceFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_allowed","type":"bool"}],"name":"setRebuyBonusCashAllowed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_swapDeadline","type":"uint256"}],"name":"setSwapDeadline","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"bool","name":"_enabled","type":"bool"}],"name":"setSwapEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_swapExecutor","type":"address"}],"name":"setSwapExecutor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tournamentId","type":"uint256"},{"internalType":"uint256","name":"_roomId","type":"uint256"},{"components":[{"internalType":"address","name":"player","type":"address"},{"internalType":"uint256","name":"multiplier","type":"uint256"}],"internalType":"struct ITournament.Result[]","name":"_results","type":"tuple[]"}],"name":"submitResults","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":"swapDeadline","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"swapEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"swapExecutor","outputs":[{"internalType":"contract ISwapExecutor","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"totalCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tournamentCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tournamentId","type":"uint256"},{"components":[{"internalType":"uint256","name":"entryFee","type":"uint256"},{"internalType":"uint256","name":"rebuyFee","type":"uint256"},{"internalType":"bool","name":"isExactFee","type":"bool"},{"internalType":"uint64","name":"entryLimit","type":"uint64"},{"internalType":"uint64","name":"maxEntriesPerRoom","type":"uint64"},{"internalType":"uint64","name":"rebuyLimit","type":"uint64"},{"internalType":"uint64","name":"startDate","type":"uint64"},{"internalType":"uint64","name":"endDate","type":"uint64"},{"internalType":"uint64","name":"ticketProfitToTickets","type":"uint64"},{"internalType":"uint64","name":"creditProfitToTickets","type":"uint64"},{"internalType":"uint64","name":"creditEntryToTickets","type":"uint64"},{"internalType":"uint64","name":"entryDuration","type":"uint64"},{"internalType":"uint64","name":"tournamentDuration","type":"uint64"},{"internalType":"uint64","name":"payoutDuration","type":"uint64"},{"internalType":"uint96","name":"creditRatio","type":"uint96"},{"internalType":"address","name":"creditId","type":"address"},{"internalType":"uint256","name":"bonusCollateral","type":"uint256"},{"internalType":"bytes32","name":"priceFeedPair","type":"bytes32"}],"internalType":"struct ITournament.Config","name":"_c","type":"tuple"}],"name":"updateTournamentConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_creditId","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawEmergencyCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_to","type":"address"}],"name":"withdrawExcess","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
61016060405234801561001157600080fd5b50604051615ae2380380615ae28339810160408190526100309161013f565b6001600160a01b0380881660805286811660a05285811660c05284811660e052838116610140528281166101005281166101205261006c610078565b505050505050506101db565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff16156100c85760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146101275780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b6001600160a01b038116811461012757600080fd5b600080600080600080600060e0888a03121561015a57600080fd5b87516101658161012a565b60208901519097506101768161012a565b60408901519096506101878161012a565b60608901519095506101988161012a565b60808901519094506101a98161012a565b60a08901519093506101ba8161012a565b60c08901519092506101cb8161012a565b8091505092959891949750929550565b60805160a05160c05160e05161010051610120516101405161580f6102d360003960008181610c620152818161162b015281816129af01528181612a1d01528181612a5e01528181613f4301526142a8015260008181610f8901526121ea0152600081816118ce015261421801526000613b0a01526000505060008181610b9601528181611556015281816119f3015281816120a201528181612315015281816123ec01528181613590015281816136810152818161377c0152818161383f0152818161387b015261394a015260008181610b03015281816114b3015281816120110152818161242e0152613a20015261580f6000f3fe6080604052600436106101eb5760003560e01c806228516c146101f057806301ffc9a7146102305780630823f6ec146102605780630b04b2121461029b5780631517b13c146102bd5780631a5bd7fc146102dd5780631ac35dc3146104b85780631dbec117146104d8578063209c8cfe14610513578063248a9ca31461053357806328754c7a1461055357806328af4ca4146106a95780632c117449146106c95780632f2ff15d146106e95780633116e4751461070957806336568abe1461071c5780633d37d8e21461073c57806347e21e6a146107a45780635aac6776146107c55780635b16fe4e146107f25780636a0706db146108125780636e2b2c7c14610832578063724e78da14610848578063741bef1a146108685780637aac643f146108955780637e059b22146108b557806384fcec46146108e257806391d1485414610902578063938a37ed1461092257806393ad9f1714610942578063942dbff4146109625780639eed5e2114610978578063a217fddf14610998578063a22271b4146109ad578063c0051d2d146109cd578063c0f80921146109ed578063c8b48f8614610a25578063d547741f14610a45578063e23f5c4814610a65578063e884269a14610a85578063f082fb2514610a9b578063f44f863814610abb578063f469a76c14610adb575b600080fd5b3480156101fc57600080fd5b5061021061020b366004614a17565b610afb565b604080519384526020840192909252908201526060015b60405180910390f35b34801561023c57600080fd5b5061025061024b366004614a58565b610d2c565b6040519015158152602001610227565b34801561026c57600080fd5b5061028d61027b366004614a82565b600b6020526000908152604090205481565b604051908152602001610227565b3480156102a757600080fd5b506102bb6102b6366004614a9f565b610d63565b005b3480156102c957600080fd5b506102bb6102d8366004614a82565b610e14565b3480156102e957600080fd5b506104ab6102f8366004614acb565b6040805161024081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081018290526101e0810182905261020081018290526102208101919091525060009081526006602081815260409283902083516102408101855281548152600182015492810192909252600281015460ff81161515948301949094526101008085046001600160401b039081166060850152600160481b860481166080850152600160881b909504851660a0840152600382015480861660c0850152600160401b808204871660e0860152600160801b808304881693860193909352600160c01b91829004871661012086015260048401548088166101408701529081048716610160860152918204861661018085015290049093166101a082015260058301546001600160601b0381166101c0830152600160601b90046001600160a01b03166101e08201529082015461020082015260079091015461022082015290565b6040516102279190614af1565b3480156104c457600080fd5b506102bb6104d3366004614e70565b610ecd565b3480156104e457600080fd5b506102506104f3366004614e9e565b600960209081526000928352604080842090915290825290205460ff1681565b34801561051f57600080fd5b506102bb61052e366004614e9e565b610f6d565b34801561053f57600080fd5b5061028d61054e366004614acb565b61115d565b34801561055f57600080fd5b5061063a61056e366004614ed7565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810191909152506000828152600760209081526040808320848452825291829020825160e08101845281548152600182015492810192909252600201546001600160401b0380821693830193909352600160401b810483166060830152600160801b81049092166080820152600160c01b820466ffffffffffffff1660a0820152600160f81b90910460ff16151560c082015292915050565b604051610227919081518152602080830151908201526040808301516001600160401b03908116918301919091526060808401518216908301526080808401519091169082015260a08083015166ffffffffffffff169082015260c09182015115159181019190915260e00190565b3480156106b557600080fd5b506102bb6106c4366004614ef9565b61117d565b3480156106d557600080fd5b506102bb6106e4366004614f8f565b611203565b3480156106f557600080fd5b506102bb61070436600461500c565b611312565b6102bb610717366004615031565b61132e565b34801561072857600080fd5b506102bb61073736600461500c565b6117c2565b34801561074857600080fd5b5061075c61075736600461504c565b6117fa565b60405161022791908151815260208083015190820152604080830151908201526060808301516001600160f81b03169082015260809182015115159181019190915260a00190565b3480156107b057600080fd5b5060035461025090600160a01b900460ff1681565b3480156107d157600080fd5b5061028d6107e0366004614a82565b600c6020526000908152604090205481565b3480156107fe57600080fd5b506102bb61080d366004614acb565b6118b4565b34801561081e57600080fd5b506102bb61082d366004614a82565b6119c4565b34801561083e57600080fd5b5061028d60025481565b34801561085457600080fd5b506102bb610863366004614a82565b611af9565b34801561087457600080fd5b50600154610888906001600160a01b031681565b6040516102279190615073565b3480156108a157600080fd5b506102bb6108b0366004615087565b611b5c565b3480156108c157600080fd5b5061028d6108d0366004614a82565b600a6020526000908152604090205481565b3480156108ee57600080fd5b50600054610888906001600160a01b031681565b34801561090e57600080fd5b5061025061091d36600461500c565b611be5565b34801561092e57600080fd5b50600354610888906001600160a01b031681565b34801561094e57600080fd5b506102bb61095d366004614acb565b611c1b565b34801561096e57600080fd5b5061028d60045481565b34801561098457600080fd5b506102bb6109933660046150df565b611c68565b3480156109a457600080fd5b5061028d600081565b3480156109b957600080fd5b5061028d6109c83660046151bd565b612160565b3480156109d957600080fd5b506102bb6109e8366004614a17565b612467565b3480156109f957600080fd5b5061028d610a0836600461500c565b600860209081526000928352604080842090915290825290205481565b348015610a3157600080fd5b506102bb610a40366004614a17565b61253a565b348015610a5157600080fd5b506102bb610a6036600461500c565b612685565b348015610a7157600080fd5b506102bb610a803660046151da565b6126a1565b348015610a9157600080fd5b5061028d60055481565b348015610aa757600080fd5b5061028d610ab63660046151f7565b612706565b348015610ac757600080fd5b506102bb610ad6366004614a9f565b6127b4565b348015610ae757600080fd5b506102bb610af6366004614ed7565b612826565b6000806000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f7888aec88886040518363ffffffff1660e01b8152600401610b4f929190615223565b602060405180830381865afa158015610b6c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b90919061523d565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f7888aec89896040518363ffffffff1660e01b8152600401610be2929190615223565b602060405180830381865afa158015610bff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c23919061523d565b604051630a3495e760e11b81526001600160a01b038a8116600483015289811660248301526044820185905260648201839052608482018990529192507f0000000000000000000000000000000000000000000000000000000000000000909116906314692bce9060a401602060405180830381865afa158015610cab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ccf919061523d565b92506000610cdd848861526c565b9050610ce98282612add565b9550610cf5868261526c565b945084831015610d205760405162461bcd60e51b8152600401610d179061527f565b60405180910390fd5b50505093509350939050565b60006001600160e01b03198216637965db0b60e01b1480610d5d57506301ffc9a760e01b6001600160e01b03198316145b92915050565b610d786001600160a01b038316333084612af3565b6001600160a01b0382166000908152600a602052604081208054839290610da09084906152af565b90915550506001600160a01b0382166000908152600b602052604081208054839290610dcd9084906152af565b90915550506040518181526001600160a01b038316907ff752bcc9ac925d5c3d43793d934c367940840c9d43a5a2dd45e22fc541b486ca9060200160405180910390a25050565b6000805160206157ba833981519152610e2c81612b4d565b6001600160a01b038216610e765760405162461bcd60e51b81526020600482015260116024820152702a1d1024b73b30b634b223b0b6b2a232bb60791b6044820152606401610d17565b600380546001600160a01b0319166001600160a01b0384161790556040517f251d193f42004ce12c536a9aea085986157caa3378ef01a05b8c3fec748ad33390610ec1908490615073565b60405180910390a15050565b6000805160206157ba833981519152610ee581612b4d565b610eee83612b5a565b610ef782612bb1565b60008381526006602052604090206003810154600160401b90046001600160401b03164210610f5c5760405162461bcd60e51b81526020600482015260116024820152702a1d102a37bab93730b6b2b73a27bb32b960791b6044820152606401610d17565b610f67818585612dd0565b50505050565b604051632474521560e21b8152600060048201523360248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906391d1485490604401602060405180830381865afa158015610fd8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ffc91906152c2565b6110405760405162461bcd60e51b815260206004820152601560248201527413db9b1e51dbdd995c9b985b98d950d85b90d85b1b605a1b6044820152606401610d17565b6001600160a01b0382166000818152600a60205260408082205490516370a0823160e01b8152919290916370a082319061107e903090600401615073565b602060405180830381865afa15801561109b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110bf919061523d565b6110c9919061526c565b9050806000036111095760405162461bcd60e51b815260206004820152600b60248201526a433a204e6f45786365737360a81b6044820152606401610d17565b61111d6001600160a01b0384168383612f5e565b7f753e7e8be98e512034401050a36a864823ca659d2f3cf27d19bcbe51e64f150e838383604051611150939291906152df565b60405180910390a1505050565b600080611168612f84565b60009384526020525050604090206001015490565b6000805160206157ba83398151915261119581612b4d565b6001600160a01b03848116600081815260096020908152604080832094881680845294825291829020805460ff191687151590811790915591519182527f5a1303b58a2f911249aacf9cad902a88e24104f1c0174b5fe0a1ebf76f8637d4910160405180910390a350505050565b60008051602061579a83398151915261121b81612b4d565b61122486612b5a565b60005b84811015611309576000878152600660209081526040808320600790925282209091908189898681811061125d5761125d615303565b90506020020135815260200190815260200160002090508060020160089054906101000a90046001600160401b03166001600160401b03166000146112b45760405162461bcd60e51b8152600401610d1790615319565b6112ff898989868181106112ca576112ca615303565b905060200201358888878181106112e3576112e3615303565b90506020020160208101906112f89190615349565b8585612fa8565b5050600101611227565b50505050505050565b61131b8261115d565b61132481612b4d565b610f678383613154565b60008051602061579a83398151915261134681612b4d565b600061136c602084018035906040860135906113629087614a82565b8660a001356131f5565b905060008161137c57600161138a565b600354600160a01b900460ff165b6020808601803560009081526006808452604080832081516102408101835281548152600182015496810196909652600281015460ff81161515928701929092526001600160401b0361010080840482166060890152600160481b840482166080890152600160881b909304811660a0880152600382015480821660c0890152600160401b808204831660e08a0152600160801b8083048416958a0195909552600160c01b9182900483166101208a015260048401548084166101408b015290810483166101608a015293840482166101808901529092049091166101a086015260058101546001600160601b0381166101c08701526001600160a01b03600160601b90910481166101e0870152918101546102008601526007015461022085015293945091927f0000000000000000000000000000000000000000000000000000000000000000169063f7888aec906114e49089614a82565b846101e001516040518363ffffffff1660e01b8152600401611507929190615223565b602060405180830381865afa158015611524573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611548919061523d565b905060006001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663f7888aec61158860208a018a614a82565b856101e001516040518363ffffffff1660e01b81526004016115ab929190615223565b602060405180830381865afa1580156115c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115ec919061523d565b905060006116128860a00135856102200151866101c001516001600160601b0316612706565b905060008561162157816116f3565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016636eca116961165d60208c018c614a82565b6101e08801516040516001600160e01b031960e085901b1681526001600160a01b03928316600482015291166024820152604481018790526064810186905260208c0135608482015260a4810185905260c4016020604051808303816000875af11580156116cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116f3919061523d565b905061172861170560208b018b614a82565b61171560808c0160608d01614a82565b8b60800135886101e0015185888a613571565b61173290846152af565b92506000611750611748368c90038c018c615364565b8484876138f7565b905061175f60208b018b614a82565b6001600160a01b03168a604001358b602001357f0eba4cfa50628f1f718bea837970d8bba1c38e0eafdfabcc2d6be94e27a75aab848c6040516117ae9291909182521515602082015260400190565b60405180910390a450505050505050505050565b6001600160a01b03811633146117eb5760405163334bd91960e11b815260040160405180910390fd5b6117f58282613bd2565b505050565b6118376040518060a0016040528060008152602001600081526020016000815260200160006001600160f81b031681526020016000151581525090565b5060008381526008602090815260408083206001600160a01b038616845282528083208484526001908101835292819020815160a081018352815481529381015492840192909252600282015490830152600301546001600160f81b0381166060830152600160f81b900460ff16151560808201525b9392505050565b6000805160206157ba8339815191526118cc81612b4d565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663155d62e16040518163ffffffff1660e01b8152600401602060405180830381865afa15801561192a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061194e919061523d565b82111561198f5760405162461bcd60e51b815260206004820152600f60248201526e543a204976616c696444657646656560881b6044820152606401610d17565b60058290556040518281527fb29b562a64adc71c1dd4e3ba67e7d8a35cbb47561abfecdf6273c7edca8bcd5e90602001610ec1565b6000805160206157ba8339815191526119dc81612b4d565b6040516350e0717960e11b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063a1c0e2f290611a28908590600401615073565b602060405180830381865afa158015611a45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a6991906152c2565b611aae5760405162461bcd60e51b81526020600482015260166024820152752a1d1024b73b30b634b229bbb0b822bc32b1baba37b960511b6044820152606401610d17565b600080546001600160a01b0319166001600160a01b0384161790556040517fc19333f7b15e9a163c9e5c7963ac3807c47e08205b8458681e2914640d2c433790610ec1908490615073565b6000805160206157ba833981519152611b1181612b4d565b600180546001600160a01b0319166001600160a01b0384161790556040517f40d8738b990e6ff9a2f56f11247e657a1e7070472260b020a4fc3ab1844787bd90610ec1908490615073565b60008051602061579a833981519152611b7481612b4d565b611b7d84612b5a565b6000848152600660209081526040808320600783528184208785529092529091206002810154600160401b90046001600160401b031615611bd05760405162461bcd60e51b8152600401610d1790615319565b611bdd8686868585612fa8565b505050505050565b600080611bf0612f84565b6000948552602090815260408086206001600160a01b03959095168652939052505090205460ff1690565b6000805160206157ba833981519152611c3381612b4d565b60048290556040518281527fee5991e8e9df89f1abe46396cca96c5c51565ceabff46dbe70ff465819582efd90602001610ec1565b60008051602061579a833981519152611c8081612b4d565b611c8984612b5a565b6000848152600660209081526040808320600783528184208785529092529091206002810154600160801b90046001600160401b0316421015611d045760405162461bcd60e51b8152602060048201526013602482015272543a20546f75726e616d656e7441637469766560681b6044820152606401610d17565b6002810154600160c01b900466ffffffffffffff164210611d5f5760405162461bcd60e51b81526020600482015260156024820152742a1d102830bcb7baba223ab930ba34b7b727bb32b960591b6044820152606401610d17565b600084516001600160401b03811115611d7a57611d7a614c51565b604051908082528060200260200182016040528015611da3578160200160208202803683370190505b509050600085516001600160401b03811115611dc157611dc1614c51565b604051908082528060200260200182016040528015611dea578160200160208202803683370190505b509050600086516001600160401b03811115611e0857611e08614c51565b604051908082528060200260200182016040528015611e31578160200160208202803683370190505b5090506000805b8851811015611f25576000898281518110611e5557611e55615303565b602002602001015190508060000151868381518110611e7657611e76615303565b60200260200101906001600160a01b031690816001600160a01b031681525050611ea28c828d8b613c4a565b868481518110611eb457611eb4615303565b60200260200101868581518110611ecd57611ecd615303565b6020026020010182815250828152505050838281518110611ef057611ef0615303565b6020026020010151858381518110611f0a57611f0a615303565b60200260200101510183019250508080600101915050611e38565b506005860154611f3e906001600160601b0316826153f3565b90508085600001541015611f6d576005860154611f6d908690600160601b90046001600160a01b031683613dbc565b80856000016000828254611f81919061526c565b90915550506001850154811115611fa357611f9e8587838d613eb9565b611ff5565b80856001016000828254611fb7919061526c565b90915550506005860154600160601b90046001600160a01b03166000908152600a602052604081208054839290611fef90849061526c565b90915550505b600586015460405163477e66f960e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169263477e66f99261205592600160601b909204909116908890889060040161540a565b600060405180830381600087803b15801561206f57600080fd5b505af1158015612083573d6000803e3d6000fd5b505050600587015460405163477e66f960e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116935063477e66f9926120e792600160601b909104909116908890879060040161540a565b600060405180830381600087803b15801561210157600080fd5b505af1158015612115573d6000803e3d6000fd5b505050507ff89b2ff43c9ce4010c9937197f818d0ad39602ac78a3bc2df09dc9a2a12ac26b8a8a8a60405161214c939291906154a5565b60405180910390a150505050505050505050565b60006000805160206157ba83398151915261217a81612b4d565b428360e001516001600160401b031610156121cd5760405162461bcd60e51b8152602060048201526013602482015272543a20456e64446174654265666f72654e6f7760681b6044820152606401610d17565b6101e083015160405163673448dd60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163673448dd9161221e9190600401615073565b602060405180830381865afa15801561223b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061225f91906152c2565b6122a05760405162461bcd60e51b8152602060048201526012602482015271150e88125b9d985b1a5910dc99591a5d125960721b6044820152606401610d17565b6122a983612bb1565b6002805460010190819055600081815260066020908152604091829020865181559086015160018201556101e08601516005820180546001600160601b0316600160601b6001600160a01b039384168102919091179182905593516326efbc1960e21b815294965091937f0000000000000000000000000000000000000000000000000000000000000000821693639bbef0649361234e939190041690600401615073565b602060405180830381865afa15801561236b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061238f919061523d565b6005820180546001600160601b0319166001600160601b039290921691909117908190556102208501516007830155604085015160028301805460ff191691151591909117905561241390600160601b90046001600160a01b03167f0000000000000000000000000000000000000000000000000000000000000000600019613fbb565b600581015461245590600160601b90046001600160a01b03167f0000000000000000000000000000000000000000000000000000000000000000600019613fbb565b612460818486612dd0565b5050919050565b6000805160206157ba83398151915261247f81612b4d565b6001600160a01b0384166000908152600b60205260409020548211156124b75760405162461bcd60e51b8152600401610d179061527f565b6001600160a01b0384166000908152600b6020526040812080548492906124df90849061526c565b909155506124f990506001600160a01b0385168484612f5e565b7fef6a77df50690de86c4d9abfd46158dfb52d59692b6aee04b98986ad9b06c36684848460405161252c939291906152df565b60405180910390a150505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b031660008115801561257f5750825b90506000826001600160401b0316600114801561259b5750303b155b9050811580156125a9575080155b156125c75760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b031916600117855583156125f057845460ff60401b1916600160401b1785555b6125fb600089613154565b506126146000805160206157ba83398151915289613154565b50600380546001600160a01b0319166001600160a01b0389161790556004869055831561267b57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b61268e8261115d565b61269781612b4d565b610f678383613bd2565b6000805160206157ba8339815191526126b981612b4d565b60038054831515600160a01b0260ff60a01b199091161790556040517fbc321a64782b0d1008d7e35b0006ba53ffa1788540684b22df1e53265c07973b90610ec190841515815260200190565b6000826127145750826118ad565b6001546040516331d98b3f60e01b8152600481018590526000916001600160a01b0316906331d98b3f90602401602060405180830381865afa15801561275e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612782919061523d565b90506000612790868361404d565b905060006127a96127a1838761406c565b6001906140bc565b979650505050505050565b6000805160206157ba8339815191526127cc81612b4d565b6001600160a01b0383166000818152600c602052604090819020849055517fa537d35addd66299c54db28c0487a6e64452d168d0581e5da24b1aec128242c5906128199085815260200190565b60405180910390a2505050565b61282f82612b5a565b600082815260066020908152604080832060078352818420858552909252909120600181015461289b5760405162461bcd60e51b81526020600482015260176024820152762a1d102737a1b7b63630ba32b930b62a37a932ba3ab93760491b6044820152606401610d17565b6002810154600160c01b900466ffffffffffffff164210156128fa5760405162461bcd60e51b81526020600482015260186024820152772a1d102830bcb7baba223ab930ba34b7b72737ba27bb32b960411b6044820152606401610d17565b6003546001820180546000918290556005850154600160601b90046001600160a01b039081168352600a60205260408320805491909416939192918391839061294490849061526c565b909155505060408051888152602081018890526001600160a01b038516818301526060810184905290517f971582322c37d0c5baa9fe414520df17514e5e007d6c23ad3fc41786a72483319181900360800190a16002840154600160f81b900460ff16156129d657507f00000000000000000000000000000000000000000000000000000000000000009150806129fc565b60035460058601546129fc916001600160a01b03600160601b9092048216911684612f5e565b8015612a42576005850154612a4290600160601b90046001600160a01b03167f000000000000000000000000000000000000000000000000000000000000000083613fbb565b60058501546040516350ecb2f560e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116926350ecb2f592612aa292600160601b909204909116908b908690600401615510565b600060405180830381600087803b158015612abc57600080fd5b505af1158015612ad0573d6000803e3d6000fd5b5050505050505050505050565b6000818310612aec57816118ad565b5090919050565b610f6784856001600160a01b03166323b872dd868686604051602401612b1b939291906152df565b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506140cb565b612b578133614125565b50565b6002548111158015612b6c5750600081115b612b575760405162461bcd60e51b8152602060048201526016602482015275150e88125b9d985b1a59151bdd5c9b985b595b9d125960521b6044820152606401610d17565b8060c001516001600160401b03168160e001516001600160401b031611612c165760405162461bcd60e51b8152602060048201526019602482015278543a20456e64446174654265666f726553746172744461746560381b6044820152606401610d17565b600081606001516001600160401b031611612c675760405162461bcd60e51b8152602060048201526011602482015270543a20456e7472794c696d69745a65726f60781b6044820152606401610d17565b670de0b6b3a76400008161010001516001600160401b03161115612c9d5760405162461bcd60e51b8152600401610d1790615531565b670de0b6b3a76400008161012001516001600160401b03161115612cd35760405162461bcd60e51b8152600401610d1790615531565b670de0b6b3a76400008161014001516001600160401b03161115612d095760405162461bcd60e51b8152600401610d1790615531565b61022081015115612b57576001546102208201516040516331d98b3f60e01b815260048101919091526000916001600160a01b0316906331d98b3f90602401602060405180830381865afa158015612d65573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d89919061523d565b11612b575760405162461bcd60e51b81526020600482015260176024820152762a1d1024b73b30b634b2283934b1b2a332b2b22830b4b960491b6044820152606401610d17565b60c081015160038401805460e0840151606085015160028801805460a0880151610100808a01516101208b01516001600160401b039a8b166001600160801b031990991698909817600160401b978b168802176001600160801b0316600160801b918b1682026001600160c01b031617600160c01b988b168902179098556101408a015160048d01805460808d0151610100600160481b03600160881b03600160c81b0319909616978c16909302600160881b600160c81b03191696909617600160881b938b169390930292909217600160481b600160881b031916600160481b938a16939093029290921790925561020088015160068b01556101a08801516101608901516101808a0151938916600160401b600160c01b039093169290921790881690950294909417600160401b600160c01b031916600160801b600160c01b031994871690930293909316919091179190931690910217905560405182907f1ca7aac7d8a61e5a730c7b0805c5d3996a1c26239939ac52aeb06703947ee63190612819908490614af1565b6117f583846001600160a01b031663a9059cbb8585604051602401612b1b92919061555c565b7f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680090565b826001600160401b0316600003612fbf5742612fc1565b825b60038301549093506001600160401b0390811690841610801590612ffc575060038201546001600160401b03600160401b9091048116908416105b61303e5760405162461bcd60e51b8152602060048201526013602482015272543a20496e76616c696453746172744461746560681b6044820152606401610d17565b600482015461305d90600160401b90046001600160401b031684615575565b600282018054600160401b600160801b031916600160401b6001600160401b039384168102919091179182905560048501546130a893600160801b9091048116929190910416615575565b600282018054600160801b600160c01b031916600160801b6001600160401b039384168102919091179182905560048501546130f393600160c01b9091048116929190910416615575565b60028201805466ffffffffffffff92909216600160c01b0266ffffffffffffff60c01b19909216919091179055604051849086907f537a110d054f03653f327661270f3d2e697f169fbeae54d58d234d298fab9a7b90600090a35050505050565b60008061315f612f84565b905061316b8484611be5565b6131eb576000848152602082815260408083206001600160a01b03871684529091529020805460ff191660011790556131a13390565b6001600160a01b0316836001600160a01b0316857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a46001915050610d5d565b6000915050610d5d565b600061320085612b5a565b6000858152600660209081526040808320600783528184208885528352818420898552600884528285206001600160a01b0389168087528186528487208b885260018101875294872090875294526002810154919490939291600160401b90046001600160401b031690036132825761327d898960008787612fa8565b6132f9565b600484015460028401546132ae916001600160401b03600160401b918290048116929190910416615594565b6001600160401b03164210156132f95760405162461bcd60e51b815260206004820152601060248201526f543a20526f6f6d4e6f7441637469766560801b6044820152606401610d17565b600283810154908301541580159650600160401b9091046001600160401b0316906000906133cd5750506001840154600284810154908601546003850154600160801b9092046001600160401b039081169392600160881b909204166001600160f81b03909116106133a45760405162461bcd60e51b8152602060048201526014602482015273150e881499589d5e531a5b5a5d14995858da195960621b6044820152606401610d17565b6003840180546001600160f81b03808216600101166001600160f81b03199091161790556134c6565b508454600286015483546101009091046001600160401b0316116134305760405162461bcd60e51b815260206004820152601a602482015279150e88141b185e595c915b9d1c9e531a5b5a5d14995858da195960321b6044820152606401610d17565b600280870154908601546001600160401b03600160481b90920482169116106134965760405162461bcd60e51b8152602060048201526018602482015277150e88149bdbdb515b9d1c9e531a5b5a5d14995858da195960421b6044820152606401610d17565b8254600190810184556002860180546001600160401b031981166001600160401b03918216909301169190911790555b8142106135125760405162461bcd60e51b815260206004820152601a6024820152792a1d102a37bab93730b6b2b73a29ba30b93a32b227b927bb32b960311b6044820152606401610d17565b600286015460ff16156135435780881461353e5760405162461bcd60e51b8152600401610d17906155b3565b613563565b808810156135635760405162461bcd60e51b8152600401610d17906155b3565b505050505050949350505050565b600034156136075760405163918a295360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063918a29539034906135cd9088908d908b9030906004016155e2565b6000604051808303818588803b1580156135e657600080fd5b505af11580156135fa573d6000803e3d6000fd5b50505050508390506127a9565b6001600160a01b03871615613751576001600160a01b0380881660009081526009602090815260408083209389168352929052205460ff1661367f5760405162461bcd60e51b8152602060048201526011602482015270150e8814ddd85c139bdd115b98589b1959607a1b6044820152606401610d17565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634da9175788878b88600454426136c091906152af565b60005460405160e088901b6001600160e01b03191681526001600160a01b039687166004820152948616602486015292851660448501526064840191909152608483015260a482018b90529190911660c482015260e401600060405180830381600087803b15801561373157600080fd5b505af1158015613745573d6000803e3d6000fd5b505050508390506127a9565b8361375c84846152af565b10156127a9576040516326efbc1960e21b81526000906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690639bbef064906137b1908990600401615073565b602060405180830381865afa1580156137ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137f2919061523d565b9050600084613801858861526c565b61380b919061526c565b9050600061381983836153f3565b90506138306001600160a01b0389168c3084612af3565b6138646001600160a01b0389167f000000000000000000000000000000000000000000000000000000000000000083613fbb565b60405163918a295360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063918a2953906138b69085908f908d9030906004016155e2565b600060405180830381600087803b1580156138d057600080fd5b505af11580156138e4573d6000803e3d6000fd5b50939d9c50505050505050505050505050565b6020808501516000908152600690915260408120816139168486612add565b90506000613924828761526c565b905081156139d15787516005840154604051638bfb07c960e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811693638bfb07c99361398c939192600160601b909104169087906004016152df565b6020604051808303816000875af11580156139ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139cf919061523d565b505b8015613a91578751600584015460405163d09df7f760e01b81526001600160a01b039283166004820152306024820152600160601b9091048216604482015260648101839052600060848201527f00000000000000000000000000000000000000000000000000000000000000009091169063d09df7f79060a4016020604051808303816000875af1158015613a6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a8f919061523d565b505b6005830154600090613aad9089906001600160601b03166153f3565b90506000613ac58a6000015186848d60200151614154565b8a5160058701546040516307c146e960e01b81526001600160a01b039283166004820152600160601b90910482166024820152336044820152606481018c90529192507f000000000000000000000000000000000000000000000000000000000000000016906307c146e990608401600060405180830381600087803b158015613b4e57600080fd5b505af1158015613b62573d6000803e3d6000fd5b50505060208b015160408c01518c516005890154613bc49450613b8e906001600160601b0316886153f3565b60058a0154613ba6906001600160601b03168a6153f3565b60058b015488908890600160601b90046001600160a01b0316614407565b9a9950505050505050505050565b600080613bdd612f84565b9050613be98484611be5565b156131eb576000848152602082815260408083206001600160a01b0387168085529252808320805460ff1916905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a46001915050610d5d565b600084815260086020908152604080832086516001600160a01b031684528252808320858452600181019092528220805483929190151580613c90575060008160010154115b613cd15760405162461bcd60e51b8152602060048201526012602482015271543a20506c617965724e6f74496e526f6f6d60701b6044820152606401610d17565b6003810154600160f81b900460ff1615613d285760405162461bcd60e51b8152602060048201526018602482015277150e8814185e5bdd5d105b1c9958591e549958d95a5d995960421b6044820152606401610d17565b6003810180546001600160f81b0316600160f81b17905560208701516000908190613d549084896144b7565b600589015460028601549294509092506001600160601b031690613d789084614739565b613d829190615609565b600588015460028501549197506001600160601b031690613da39083614739565b613dad9190615609565b94505050505094509492505050565b8254600090613dcb908361526c565b6001600160a01b0384166000908152600c60205260409020549091508111801590613e0e57506001600160a01b0383166000908152600b60205260409020548111155b15613e7a5780846000016000828254613e2791906152af565b9250508190555080846001016000828254613e4291906152af565b90915550506001600160a01b0383166000908152600b602052604081208054839290613e6f90849061526c565b90915550610f679050565b60405162461bcd60e51b8152602060048201526014602482015273150e88141c9a5e99541bdbdb115e18d95959195960621b6044820152606401610d17565b6000613ed9856001015484613ece919061526c565b6006860154906140bc565b6001860180548583039081019091556005860180546001600160a01b03600160601b9182900481166000908152600a602052604090819020805490950190945560028a0180546001600160f81b0316600160f81b17905591549251632d9f6c6f60e21b81529394507f000000000000000000000000000000000000000000000000000000000000000082169363b67db1bc93613f82939290049091169086908690600401615510565b600060405180830381600087803b158015613f9c57600080fd5b505af1158015613fb0573d6000803e3d6000fd5b505050505050505050565b6000836001600160a01b031663095ea7b38484604051602401613fdf92919061555c565b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050509050614018848261474e565b610f675761404384856001600160a01b031663095ea7b3866000604051602401612b1b92919061555c565b610f6784826140cb565b60008161406284670de0b6b3a76400006153f3565b6118ad9190615609565b600081600003614087576140808284615609565b9050610d5d565b82156140b3578161409960018561526c565b6140a39190615609565b6140ae9060016152af565b6118ad565b50600092915050565b6000818311612aec57816118ad565b60006140e06001600160a01b038416836147f6565b9050805160001415801561410557508080602001905181019061410391906152c2565b155b156117f55782604051635274afe760e01b8152600401610d179190615073565b61412f8282611be5565b61415057808260405163e2517d3f60e01b8152600401610d1792919061555c565b5050565b60058301546040516370a0823160e01b81526000918291600160601b9091046001600160a01b0316906370a0823190614191903090600401615073565b602060405180830381865afa1580156141ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141d2919061523d565b600354600554604051630dac272b60e21b81526001600160a01b0392831660048201528983166024820152604481018890526064810191909152919250600091829182917f0000000000000000000000000000000000000000000000000000000000000000909116906336b09cac906084016000604051808303816000875af1158015614263573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261428b9190810190615693565b925092509250808410156143355760058801546001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169163b67db1bc91600160601b90910416886142e4888661526c565b6040518463ffffffff1660e01b815260040161430293929190615510565b600060405180830381600087803b15801561431c57600080fd5b505af1158015614330573d6000803e3d6000fd5b505050505b60005b83518110156143fa5760006001600160a01b031684828151811061435e5761435e615303565b60200260200101516001600160a01b031614158015614397575082818151811061438a5761438a615303565b6020026020010151600014155b156143f2576143f28482815181106143b1576143b1615303565b60200260200101518483815181106143cb576143cb615303565b602090810291909101015160058c0154600160601b90046001600160a01b03169190612f5e565b600101614338565b5098975050505050505050565b60008881526007602090815260408083208a845282528083208b8452600883528184206001600160a01b038b16855283528184208b8552600101909252822084860392888801906144588288612add565b84548601855560019485018054919093039081019092555060028201805490970190965580549097018755959095018054909401909355506001600160a01b039091166000908152600a60205260409020805490910190559392505050565b60028201548254600091829182906144cf908361404d565b905060006144ea83886001015461404d90919063ffffffff16565b9050670de0b6b3a7640000881115614643576040805161024081018252875481526001808901546020830152600289015460ff81161515938301939093526101008084046001600160401b039081166060850152600160481b850481166080850152600160881b909404841660a084015260038a015480851660c0850152600160401b808204861660e0860152600160801b808304871693860193909352600160c01b91829004861661012086015260048c01548087166101408701529081048616610160860152918204851661018085015290049092166101a082015260058801546001600160601b0381166101c0830152600160601b90046001600160a01b03166101e082015260068801546102008201526007880154610220820152885491890154614639928b9291878661462a89670de0b6b3a764000061526c565b614634919061526c565b614804565b909550935061472e565b8761464e82846152af565b116146e0576004860154600188015460009161467f91869161467991906001600160401b0316614739565b9061404d565b60048801549091506000906146a5906001600160401b0316670de0b6b3a7640000615594565b6001600160401b031690506146ba82856152af565b96506146d785614679838c6001015461473990919063ffffffff16565b9550505061472e565b87821161472a5760006146f3838a61526c565b60048801549091506000906147129083906001600160401b0316614739565b905061471e81856152af565b96506146d7878b61526c565b8794505b505050935093915050565b6000670de0b6b3a764000061406283856153f3565b6000806000846001600160a01b03168460405161476b919061576a565b6000604051808303816000865af19150503d80600081146147a8576040519150601f19603f3d011682016040523d82523d6000602084013e6147ad565b606091505b50915091508180156147d75750805115806147d75750808060200190518101906147d791906152c2565b80156147ed57506000856001600160a01b03163b115b95945050505050565b60606118ad838360006148de565b6000808061481a670de0b6b3a76400008a61526c565b9050600061483f8961010001516001600160401b03168361473990919063ffffffff16565b905060006148648a61012001516001600160401b03168461473990919063ffffffff16565b9050600061488461487d84670de0b6b3a76400006152af565b8b90614739565b905060006148a5838d61014001516001600160401b031661487d91906152af565b90506148b58961467983856152af565b9650876148c2888f61526c565b6148cc919061526c565b95505050505050965096945050505050565b606081471015614903573060405163cd78605960e01b8152600401610d179190615073565b600080856001600160a01b0316848660405161491f919061576a565b60006040518083038185875af1925050503d806000811461495c576040519150601f19603f3d011682016040523d82523d6000602084013e614961565b606091505b509150915061497186838361497b565b9695505050505050565b60608261498b576140ae826149c9565b81511580156149a257506001600160a01b0384163b155b156149c25783604051639996b31560e01b8152600401610d179190615073565b50806118ad565b8051156149d95780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b0381168114612b5757600080fd5b8035614a12816149f2565b919050565b600080600060608486031215614a2c57600080fd5b8335614a37816149f2565b92506020840135614a47816149f2565b929592945050506040919091013590565b600060208284031215614a6a57600080fd5b81356001600160e01b0319811681146118ad57600080fd5b600060208284031215614a9457600080fd5b81356118ad816149f2565b60008060408385031215614ab257600080fd5b8235614abd816149f2565b946020939093013593505050565b600060208284031215614add57600080fd5b5035919050565b6001600160401b03169052565b60006102408201905082518252602083015160208301526040830151614b1b604084018215159052565b506060830151614b2e6060840182614ae4565b506080830151614b416080840182614ae4565b5060a0830151614b5460a0840182614ae4565b5060c0830151614b6760c0840182614ae4565b5060e0830151614b7a60e0840182614ae4565b50610100830151614b8f610100840182614ae4565b50610120830151614ba4610120840182614ae4565b50610140830151614bb9610140840182614ae4565b50610160830151614bce610160840182614ae4565b50610180830151614be3610180840182614ae4565b506101a0830151614bf86101a0840182614ae4565b506101c0830151614c156101c08401826001600160601b03169052565b506101e0830151614c326101e08401826001600160a01b03169052565b5061020083015161020083015261022083015161022083015292915050565b634e487b7160e01b600052604160045260246000fd5b60405161024081016001600160401b0381118282101715614c8a57614c8a614c51565b60405290565b604080519081016001600160401b0381118282101715614c8a57614c8a614c51565b604051601f8201601f191681016001600160401b0381118282101715614cda57614cda614c51565b604052919050565b8015158114612b5757600080fd5b8035614a1281614ce2565b80356001600160401b0381168114614a1257600080fd5b80356001600160601b0381168114614a1257600080fd5b60006102408284031215614d3c57600080fd5b614d44614c67565b82358152602080840135908201529050614d6060408301614cf0565b6040820152614d7160608301614cfb565b6060820152614d8260808301614cfb565b6080820152614d9360a08301614cfb565b60a0820152614da460c08301614cfb565b60c0820152614db560e08301614cfb565b60e0820152614dc76101008301614cfb565b610100820152614dda6101208301614cfb565b610120820152614ded6101408301614cfb565b610140820152614e006101608301614cfb565b610160820152614e136101808301614cfb565b610180820152614e266101a08301614cfb565b6101a0820152614e396101c08301614d12565b6101c0820152614e4c6101e08301614a07565b6101e082015261020082810135908201526102209182013591810191909152919050565b6000806102608385031215614e8457600080fd5b82359150614e958460208501614d29565b90509250929050565b60008060408385031215614eb157600080fd5b8235614ebc816149f2565b91506020830135614ecc816149f2565b809150509250929050565b60008060408385031215614eea57600080fd5b50508035926020909101359150565b600080600060608486031215614f0e57600080fd5b8335614f19816149f2565b92506020840135614f29816149f2565b91506040840135614f3981614ce2565b809150509250925092565b60008083601f840112614f5657600080fd5b5081356001600160401b03811115614f6d57600080fd5b6020830191508360208260051b8501011115614f8857600080fd5b9250929050565b600080600080600060608688031215614fa757600080fd5b8535945060208601356001600160401b03811115614fc457600080fd5b614fd088828901614f44565b90955093505060408601356001600160401b03811115614fef57600080fd5b614ffb88828901614f44565b969995985093965092949392505050565b6000806040838503121561501f57600080fd5b823591506020830135614ecc816149f2565b600060c082840312801561504457600080fd5b509092915050565b60008060006060848603121561506157600080fd5b833592506020840135614a47816149f2565b6001600160a01b0391909116815260200190565b60008060006060848603121561509c57600080fd5b83359250602084013591506150b360408501614cfb565b90509250925092565b60006001600160401b038211156150d5576150d5614c51565b5060051b60200190565b6000806000606084860312156150f457600080fd5b833592506020840135915060408401356001600160401b0381111561511857600080fd5b8401601f8101861361512957600080fd5b803561513c615137826150bc565b614cb2565b8082825260208201915060208360061b85010192508883111561515e57600080fd5b6020840193505b828410156151af576040848a03121561517d57600080fd5b615185614c90565b8435615190816149f2565b8152602085810135818301529083526040909401939190910190615165565b809450505050509250925092565b600061024082840312156151d057600080fd5b6118ad8383614d29565b6000602082840312156151ec57600080fd5b81356118ad81614ce2565b60008060006060848603121561520c57600080fd5b505081359360208301359350604090920135919050565b6001600160a01b0392831681529116602082015260400190565b60006020828403121561524f57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b81810381811115610d5d57610d5d615256565b602080825260169082015275543a20496e73756666696369656e7442616c616e636560501b604082015260600190565b80820180821115610d5d57610d5d615256565b6000602082840312156152d457600080fd5b81516118ad81614ce2565b6001600160a01b039384168152919092166020820152604081019190915260600190565b634e487b7160e01b600052603260045260246000fd5b602080825260169082015275543a20526f6f6d20616c72656164792061637469766560501b604082015260600190565b60006020828403121561535b57600080fd5b6118ad82614cfb565b600060c082840312801561537757600080fd5b5060405160009060c081016001600160401b038111828210171561539d5761539d614c51565b60405283356153ab816149f2565b8152602084810135908201526040808501359082015260608401356153cf816149f2565b60608201526080848101359082015260a09384013593810193909352509092915050565b8082028115828204841417610d5d57610d5d615256565b6001600160a01b0384168152606060208083018290528451918301829052600091908501906080840190835b8181101561545d5783516001600160a01b0316835260209384019390920191600101615436565b505083810360408501528451808252602091820192509085019060005b8181101561549857825184526020938401939092019160010161547a565b5091979650505050505050565b6000606082018583528460208401526060604084015280845180835260808501915060208601925060005b8181101561550357835180516001600160a01b0316845260209081015181850152909301926040909201916001016154d0565b5090979650505050505050565b6001600160a01b039390931683526020830191909152604082015260600190565b602080825260119082015270150e88125b9d985b1a5914195c98d95b9d607a1b604082015260600190565b6001600160a01b03929092168252602082015260400190565b6001600160401b038181168382160190811115610d5d57610d5d615256565b6001600160401b038281168282160390811115610d5d57610d5d615256565b602080825260159082015274150e88125b9d985b1a59115b9d1c9e505b5bdd5b9d605a1b604082015260600190565b9384526001600160a01b039283166020850152908216604084015216606082015260800190565b60008261562657634e487b7160e01b600052601260045260246000fd5b500490565b600082601f83011261563c57600080fd5b815161564a615137826150bc565b8082825260208201915060208360051b86010192508583111561566c57600080fd5b602085015b83811015615689578051835260209283019201615671565b5095945050505050565b6000806000606084860312156156a857600080fd5b83516001600160401b038111156156be57600080fd5b8401601f810186136156cf57600080fd5b80516156dd615137826150bc565b8082825260208201915060208360051b8501019250888311156156ff57600080fd5b6020840193505b8284101561572a578351615719816149f2565b825260209384019390910190615706565b6020880151909650925050506001600160401b0381111561574a57600080fd5b6157568682870161562b565b604095909501519396949550929392505050565b6000825160005b8181101561578b5760208186018101518583015201615771565b50600092019182525091905056fee2b7fb3b832174769106daebcfd6d1970523240dda11281102db9363b83b0dc4a49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775a2646970667358221220b103b14044fec890ce2f026e73a0752823755a8d962612203f13ea42b0d8c09564736f6c634300081b003300000000000000000000000087c85a8b87cc703fb4dd32b154896b2fa9e1980d000000000000000000000000d2e0b028489e24ed832f8f7d95353565d1efbde80000000000000000000000000ae1a1eb1950b037cccab33218e44e8d99fd72e80000000000000000000000009f991d9c34a63af89e8e09befbe2148f66eab163000000000000000000000000a73685da714cfadba5d23bf38973306d6ff560c7000000000000000000000000699dca792d637dace2ca5f6b2c77382db6e94b7e0000000000000000000000008b42bab5cfe04931b433f105338582e0a07dc26d
Deployed Bytecode
0x6080604052600436106101eb5760003560e01c806228516c146101f057806301ffc9a7146102305780630823f6ec146102605780630b04b2121461029b5780631517b13c146102bd5780631a5bd7fc146102dd5780631ac35dc3146104b85780631dbec117146104d8578063209c8cfe14610513578063248a9ca31461053357806328754c7a1461055357806328af4ca4146106a95780632c117449146106c95780632f2ff15d146106e95780633116e4751461070957806336568abe1461071c5780633d37d8e21461073c57806347e21e6a146107a45780635aac6776146107c55780635b16fe4e146107f25780636a0706db146108125780636e2b2c7c14610832578063724e78da14610848578063741bef1a146108685780637aac643f146108955780637e059b22146108b557806384fcec46146108e257806391d1485414610902578063938a37ed1461092257806393ad9f1714610942578063942dbff4146109625780639eed5e2114610978578063a217fddf14610998578063a22271b4146109ad578063c0051d2d146109cd578063c0f80921146109ed578063c8b48f8614610a25578063d547741f14610a45578063e23f5c4814610a65578063e884269a14610a85578063f082fb2514610a9b578063f44f863814610abb578063f469a76c14610adb575b600080fd5b3480156101fc57600080fd5b5061021061020b366004614a17565b610afb565b604080519384526020840192909252908201526060015b60405180910390f35b34801561023c57600080fd5b5061025061024b366004614a58565b610d2c565b6040519015158152602001610227565b34801561026c57600080fd5b5061028d61027b366004614a82565b600b6020526000908152604090205481565b604051908152602001610227565b3480156102a757600080fd5b506102bb6102b6366004614a9f565b610d63565b005b3480156102c957600080fd5b506102bb6102d8366004614a82565b610e14565b3480156102e957600080fd5b506104ab6102f8366004614acb565b6040805161024081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081018290526101e0810182905261020081018290526102208101919091525060009081526006602081815260409283902083516102408101855281548152600182015492810192909252600281015460ff81161515948301949094526101008085046001600160401b039081166060850152600160481b860481166080850152600160881b909504851660a0840152600382015480861660c0850152600160401b808204871660e0860152600160801b808304881693860193909352600160c01b91829004871661012086015260048401548088166101408701529081048716610160860152918204861661018085015290049093166101a082015260058301546001600160601b0381166101c0830152600160601b90046001600160a01b03166101e08201529082015461020082015260079091015461022082015290565b6040516102279190614af1565b3480156104c457600080fd5b506102bb6104d3366004614e70565b610ecd565b3480156104e457600080fd5b506102506104f3366004614e9e565b600960209081526000928352604080842090915290825290205460ff1681565b34801561051f57600080fd5b506102bb61052e366004614e9e565b610f6d565b34801561053f57600080fd5b5061028d61054e366004614acb565b61115d565b34801561055f57600080fd5b5061063a61056e366004614ed7565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810191909152506000828152600760209081526040808320848452825291829020825160e08101845281548152600182015492810192909252600201546001600160401b0380821693830193909352600160401b810483166060830152600160801b81049092166080820152600160c01b820466ffffffffffffff1660a0820152600160f81b90910460ff16151560c082015292915050565b604051610227919081518152602080830151908201526040808301516001600160401b03908116918301919091526060808401518216908301526080808401519091169082015260a08083015166ffffffffffffff169082015260c09182015115159181019190915260e00190565b3480156106b557600080fd5b506102bb6106c4366004614ef9565b61117d565b3480156106d557600080fd5b506102bb6106e4366004614f8f565b611203565b3480156106f557600080fd5b506102bb61070436600461500c565b611312565b6102bb610717366004615031565b61132e565b34801561072857600080fd5b506102bb61073736600461500c565b6117c2565b34801561074857600080fd5b5061075c61075736600461504c565b6117fa565b60405161022791908151815260208083015190820152604080830151908201526060808301516001600160f81b03169082015260809182015115159181019190915260a00190565b3480156107b057600080fd5b5060035461025090600160a01b900460ff1681565b3480156107d157600080fd5b5061028d6107e0366004614a82565b600c6020526000908152604090205481565b3480156107fe57600080fd5b506102bb61080d366004614acb565b6118b4565b34801561081e57600080fd5b506102bb61082d366004614a82565b6119c4565b34801561083e57600080fd5b5061028d60025481565b34801561085457600080fd5b506102bb610863366004614a82565b611af9565b34801561087457600080fd5b50600154610888906001600160a01b031681565b6040516102279190615073565b3480156108a157600080fd5b506102bb6108b0366004615087565b611b5c565b3480156108c157600080fd5b5061028d6108d0366004614a82565b600a6020526000908152604090205481565b3480156108ee57600080fd5b50600054610888906001600160a01b031681565b34801561090e57600080fd5b5061025061091d36600461500c565b611be5565b34801561092e57600080fd5b50600354610888906001600160a01b031681565b34801561094e57600080fd5b506102bb61095d366004614acb565b611c1b565b34801561096e57600080fd5b5061028d60045481565b34801561098457600080fd5b506102bb6109933660046150df565b611c68565b3480156109a457600080fd5b5061028d600081565b3480156109b957600080fd5b5061028d6109c83660046151bd565b612160565b3480156109d957600080fd5b506102bb6109e8366004614a17565b612467565b3480156109f957600080fd5b5061028d610a0836600461500c565b600860209081526000928352604080842090915290825290205481565b348015610a3157600080fd5b506102bb610a40366004614a17565b61253a565b348015610a5157600080fd5b506102bb610a6036600461500c565b612685565b348015610a7157600080fd5b506102bb610a803660046151da565b6126a1565b348015610a9157600080fd5b5061028d60055481565b348015610aa757600080fd5b5061028d610ab63660046151f7565b612706565b348015610ac757600080fd5b506102bb610ad6366004614a9f565b6127b4565b348015610ae757600080fd5b506102bb610af6366004614ed7565b612826565b6000806000807f00000000000000000000000087c85a8b87cc703fb4dd32b154896b2fa9e1980d6001600160a01b031663f7888aec88886040518363ffffffff1660e01b8152600401610b4f929190615223565b602060405180830381865afa158015610b6c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b90919061523d565b905060007f000000000000000000000000d2e0b028489e24ed832f8f7d95353565d1efbde86001600160a01b031663f7888aec89896040518363ffffffff1660e01b8152600401610be2929190615223565b602060405180830381865afa158015610bff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c23919061523d565b604051630a3495e760e11b81526001600160a01b038a8116600483015289811660248301526044820185905260648201839052608482018990529192507f000000000000000000000000a73685da714cfadba5d23bf38973306d6ff560c7909116906314692bce9060a401602060405180830381865afa158015610cab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ccf919061523d565b92506000610cdd848861526c565b9050610ce98282612add565b9550610cf5868261526c565b945084831015610d205760405162461bcd60e51b8152600401610d179061527f565b60405180910390fd5b50505093509350939050565b60006001600160e01b03198216637965db0b60e01b1480610d5d57506301ffc9a760e01b6001600160e01b03198316145b92915050565b610d786001600160a01b038316333084612af3565b6001600160a01b0382166000908152600a602052604081208054839290610da09084906152af565b90915550506001600160a01b0382166000908152600b602052604081208054839290610dcd9084906152af565b90915550506040518181526001600160a01b038316907ff752bcc9ac925d5c3d43793d934c367940840c9d43a5a2dd45e22fc541b486ca9060200160405180910390a25050565b6000805160206157ba833981519152610e2c81612b4d565b6001600160a01b038216610e765760405162461bcd60e51b81526020600482015260116024820152702a1d1024b73b30b634b223b0b6b2a232bb60791b6044820152606401610d17565b600380546001600160a01b0319166001600160a01b0384161790556040517f251d193f42004ce12c536a9aea085986157caa3378ef01a05b8c3fec748ad33390610ec1908490615073565b60405180910390a15050565b6000805160206157ba833981519152610ee581612b4d565b610eee83612b5a565b610ef782612bb1565b60008381526006602052604090206003810154600160401b90046001600160401b03164210610f5c5760405162461bcd60e51b81526020600482015260116024820152702a1d102a37bab93730b6b2b73a27bb32b960791b6044820152606401610d17565b610f67818585612dd0565b50505050565b604051632474521560e21b8152600060048201523360248201527f0000000000000000000000008b42bab5cfe04931b433f105338582e0a07dc26d6001600160a01b0316906391d1485490604401602060405180830381865afa158015610fd8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ffc91906152c2565b6110405760405162461bcd60e51b815260206004820152601560248201527413db9b1e51dbdd995c9b985b98d950d85b90d85b1b605a1b6044820152606401610d17565b6001600160a01b0382166000818152600a60205260408082205490516370a0823160e01b8152919290916370a082319061107e903090600401615073565b602060405180830381865afa15801561109b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110bf919061523d565b6110c9919061526c565b9050806000036111095760405162461bcd60e51b815260206004820152600b60248201526a433a204e6f45786365737360a81b6044820152606401610d17565b61111d6001600160a01b0384168383612f5e565b7f753e7e8be98e512034401050a36a864823ca659d2f3cf27d19bcbe51e64f150e838383604051611150939291906152df565b60405180910390a1505050565b600080611168612f84565b60009384526020525050604090206001015490565b6000805160206157ba83398151915261119581612b4d565b6001600160a01b03848116600081815260096020908152604080832094881680845294825291829020805460ff191687151590811790915591519182527f5a1303b58a2f911249aacf9cad902a88e24104f1c0174b5fe0a1ebf76f8637d4910160405180910390a350505050565b60008051602061579a83398151915261121b81612b4d565b61122486612b5a565b60005b84811015611309576000878152600660209081526040808320600790925282209091908189898681811061125d5761125d615303565b90506020020135815260200190815260200160002090508060020160089054906101000a90046001600160401b03166001600160401b03166000146112b45760405162461bcd60e51b8152600401610d1790615319565b6112ff898989868181106112ca576112ca615303565b905060200201358888878181106112e3576112e3615303565b90506020020160208101906112f89190615349565b8585612fa8565b5050600101611227565b50505050505050565b61131b8261115d565b61132481612b4d565b610f678383613154565b60008051602061579a83398151915261134681612b4d565b600061136c602084018035906040860135906113629087614a82565b8660a001356131f5565b905060008161137c57600161138a565b600354600160a01b900460ff165b6020808601803560009081526006808452604080832081516102408101835281548152600182015496810196909652600281015460ff81161515928701929092526001600160401b0361010080840482166060890152600160481b840482166080890152600160881b909304811660a0880152600382015480821660c0890152600160401b808204831660e08a0152600160801b8083048416958a0195909552600160c01b9182900483166101208a015260048401548084166101408b015290810483166101608a015293840482166101808901529092049091166101a086015260058101546001600160601b0381166101c08701526001600160a01b03600160601b90910481166101e0870152918101546102008601526007015461022085015293945091927f00000000000000000000000087c85a8b87cc703fb4dd32b154896b2fa9e1980d169063f7888aec906114e49089614a82565b846101e001516040518363ffffffff1660e01b8152600401611507929190615223565b602060405180830381865afa158015611524573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611548919061523d565b905060006001600160a01b037f000000000000000000000000d2e0b028489e24ed832f8f7d95353565d1efbde81663f7888aec61158860208a018a614a82565b856101e001516040518363ffffffff1660e01b81526004016115ab929190615223565b602060405180830381865afa1580156115c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115ec919061523d565b905060006116128860a00135856102200151866101c001516001600160601b0316612706565b905060008561162157816116f3565b6001600160a01b037f000000000000000000000000a73685da714cfadba5d23bf38973306d6ff560c716636eca116961165d60208c018c614a82565b6101e08801516040516001600160e01b031960e085901b1681526001600160a01b03928316600482015291166024820152604481018790526064810186905260208c0135608482015260a4810185905260c4016020604051808303816000875af11580156116cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116f3919061523d565b905061172861170560208b018b614a82565b61171560808c0160608d01614a82565b8b60800135886101e0015185888a613571565b61173290846152af565b92506000611750611748368c90038c018c615364565b8484876138f7565b905061175f60208b018b614a82565b6001600160a01b03168a604001358b602001357f0eba4cfa50628f1f718bea837970d8bba1c38e0eafdfabcc2d6be94e27a75aab848c6040516117ae9291909182521515602082015260400190565b60405180910390a450505050505050505050565b6001600160a01b03811633146117eb5760405163334bd91960e11b815260040160405180910390fd5b6117f58282613bd2565b505050565b6118376040518060a0016040528060008152602001600081526020016000815260200160006001600160f81b031681526020016000151581525090565b5060008381526008602090815260408083206001600160a01b038616845282528083208484526001908101835292819020815160a081018352815481529381015492840192909252600282015490830152600301546001600160f81b0381166060830152600160f81b900460ff16151560808201525b9392505050565b6000805160206157ba8339815191526118cc81612b4d565b7f000000000000000000000000699dca792d637dace2ca5f6b2c77382db6e94b7e6001600160a01b031663155d62e16040518163ffffffff1660e01b8152600401602060405180830381865afa15801561192a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061194e919061523d565b82111561198f5760405162461bcd60e51b815260206004820152600f60248201526e543a204976616c696444657646656560881b6044820152606401610d17565b60058290556040518281527fb29b562a64adc71c1dd4e3ba67e7d8a35cbb47561abfecdf6273c7edca8bcd5e90602001610ec1565b6000805160206157ba8339815191526119dc81612b4d565b6040516350e0717960e11b81526001600160a01b037f000000000000000000000000d2e0b028489e24ed832f8f7d95353565d1efbde8169063a1c0e2f290611a28908590600401615073565b602060405180830381865afa158015611a45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a6991906152c2565b611aae5760405162461bcd60e51b81526020600482015260166024820152752a1d1024b73b30b634b229bbb0b822bc32b1baba37b960511b6044820152606401610d17565b600080546001600160a01b0319166001600160a01b0384161790556040517fc19333f7b15e9a163c9e5c7963ac3807c47e08205b8458681e2914640d2c433790610ec1908490615073565b6000805160206157ba833981519152611b1181612b4d565b600180546001600160a01b0319166001600160a01b0384161790556040517f40d8738b990e6ff9a2f56f11247e657a1e7070472260b020a4fc3ab1844787bd90610ec1908490615073565b60008051602061579a833981519152611b7481612b4d565b611b7d84612b5a565b6000848152600660209081526040808320600783528184208785529092529091206002810154600160401b90046001600160401b031615611bd05760405162461bcd60e51b8152600401610d1790615319565b611bdd8686868585612fa8565b505050505050565b600080611bf0612f84565b6000948552602090815260408086206001600160a01b03959095168652939052505090205460ff1690565b6000805160206157ba833981519152611c3381612b4d565b60048290556040518281527fee5991e8e9df89f1abe46396cca96c5c51565ceabff46dbe70ff465819582efd90602001610ec1565b60008051602061579a833981519152611c8081612b4d565b611c8984612b5a565b6000848152600660209081526040808320600783528184208785529092529091206002810154600160801b90046001600160401b0316421015611d045760405162461bcd60e51b8152602060048201526013602482015272543a20546f75726e616d656e7441637469766560681b6044820152606401610d17565b6002810154600160c01b900466ffffffffffffff164210611d5f5760405162461bcd60e51b81526020600482015260156024820152742a1d102830bcb7baba223ab930ba34b7b727bb32b960591b6044820152606401610d17565b600084516001600160401b03811115611d7a57611d7a614c51565b604051908082528060200260200182016040528015611da3578160200160208202803683370190505b509050600085516001600160401b03811115611dc157611dc1614c51565b604051908082528060200260200182016040528015611dea578160200160208202803683370190505b509050600086516001600160401b03811115611e0857611e08614c51565b604051908082528060200260200182016040528015611e31578160200160208202803683370190505b5090506000805b8851811015611f25576000898281518110611e5557611e55615303565b602002602001015190508060000151868381518110611e7657611e76615303565b60200260200101906001600160a01b031690816001600160a01b031681525050611ea28c828d8b613c4a565b868481518110611eb457611eb4615303565b60200260200101868581518110611ecd57611ecd615303565b6020026020010182815250828152505050838281518110611ef057611ef0615303565b6020026020010151858381518110611f0a57611f0a615303565b60200260200101510183019250508080600101915050611e38565b506005860154611f3e906001600160601b0316826153f3565b90508085600001541015611f6d576005860154611f6d908690600160601b90046001600160a01b031683613dbc565b80856000016000828254611f81919061526c565b90915550506001850154811115611fa357611f9e8587838d613eb9565b611ff5565b80856001016000828254611fb7919061526c565b90915550506005860154600160601b90046001600160a01b03166000908152600a602052604081208054839290611fef90849061526c565b90915550505b600586015460405163477e66f960e01b81526001600160a01b037f00000000000000000000000087c85a8b87cc703fb4dd32b154896b2fa9e1980d81169263477e66f99261205592600160601b909204909116908890889060040161540a565b600060405180830381600087803b15801561206f57600080fd5b505af1158015612083573d6000803e3d6000fd5b505050600587015460405163477e66f960e01b81526001600160a01b037f000000000000000000000000d2e0b028489e24ed832f8f7d95353565d1efbde88116935063477e66f9926120e792600160601b909104909116908890879060040161540a565b600060405180830381600087803b15801561210157600080fd5b505af1158015612115573d6000803e3d6000fd5b505050507ff89b2ff43c9ce4010c9937197f818d0ad39602ac78a3bc2df09dc9a2a12ac26b8a8a8a60405161214c939291906154a5565b60405180910390a150505050505050505050565b60006000805160206157ba83398151915261217a81612b4d565b428360e001516001600160401b031610156121cd5760405162461bcd60e51b8152602060048201526013602482015272543a20456e64446174654265666f72654e6f7760681b6044820152606401610d17565b6101e083015160405163673448dd60e01b81526001600160a01b037f0000000000000000000000008b42bab5cfe04931b433f105338582e0a07dc26d169163673448dd9161221e9190600401615073565b602060405180830381865afa15801561223b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061225f91906152c2565b6122a05760405162461bcd60e51b8152602060048201526012602482015271150e88125b9d985b1a5910dc99591a5d125960721b6044820152606401610d17565b6122a983612bb1565b6002805460010190819055600081815260066020908152604091829020865181559086015160018201556101e08601516005820180546001600160601b0316600160601b6001600160a01b039384168102919091179182905593516326efbc1960e21b815294965091937f000000000000000000000000d2e0b028489e24ed832f8f7d95353565d1efbde8821693639bbef0649361234e939190041690600401615073565b602060405180830381865afa15801561236b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061238f919061523d565b6005820180546001600160601b0319166001600160601b039290921691909117908190556102208501516007830155604085015160028301805460ff191691151591909117905561241390600160601b90046001600160a01b03167f000000000000000000000000d2e0b028489e24ed832f8f7d95353565d1efbde8600019613fbb565b600581015461245590600160601b90046001600160a01b03167f00000000000000000000000087c85a8b87cc703fb4dd32b154896b2fa9e1980d600019613fbb565b612460818486612dd0565b5050919050565b6000805160206157ba83398151915261247f81612b4d565b6001600160a01b0384166000908152600b60205260409020548211156124b75760405162461bcd60e51b8152600401610d179061527f565b6001600160a01b0384166000908152600b6020526040812080548492906124df90849061526c565b909155506124f990506001600160a01b0385168484612f5e565b7fef6a77df50690de86c4d9abfd46158dfb52d59692b6aee04b98986ad9b06c36684848460405161252c939291906152df565b60405180910390a150505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b031660008115801561257f5750825b90506000826001600160401b0316600114801561259b5750303b155b9050811580156125a9575080155b156125c75760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b031916600117855583156125f057845460ff60401b1916600160401b1785555b6125fb600089613154565b506126146000805160206157ba83398151915289613154565b50600380546001600160a01b0319166001600160a01b0389161790556004869055831561267b57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b61268e8261115d565b61269781612b4d565b610f678383613bd2565b6000805160206157ba8339815191526126b981612b4d565b60038054831515600160a01b0260ff60a01b199091161790556040517fbc321a64782b0d1008d7e35b0006ba53ffa1788540684b22df1e53265c07973b90610ec190841515815260200190565b6000826127145750826118ad565b6001546040516331d98b3f60e01b8152600481018590526000916001600160a01b0316906331d98b3f90602401602060405180830381865afa15801561275e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612782919061523d565b90506000612790868361404d565b905060006127a96127a1838761406c565b6001906140bc565b979650505050505050565b6000805160206157ba8339815191526127cc81612b4d565b6001600160a01b0383166000818152600c602052604090819020849055517fa537d35addd66299c54db28c0487a6e64452d168d0581e5da24b1aec128242c5906128199085815260200190565b60405180910390a2505050565b61282f82612b5a565b600082815260066020908152604080832060078352818420858552909252909120600181015461289b5760405162461bcd60e51b81526020600482015260176024820152762a1d102737a1b7b63630ba32b930b62a37a932ba3ab93760491b6044820152606401610d17565b6002810154600160c01b900466ffffffffffffff164210156128fa5760405162461bcd60e51b81526020600482015260186024820152772a1d102830bcb7baba223ab930ba34b7b72737ba27bb32b960411b6044820152606401610d17565b6003546001820180546000918290556005850154600160601b90046001600160a01b039081168352600a60205260408320805491909416939192918391839061294490849061526c565b909155505060408051888152602081018890526001600160a01b038516818301526060810184905290517f971582322c37d0c5baa9fe414520df17514e5e007d6c23ad3fc41786a72483319181900360800190a16002840154600160f81b900460ff16156129d657507f000000000000000000000000a73685da714cfadba5d23bf38973306d6ff560c79150806129fc565b60035460058601546129fc916001600160a01b03600160601b9092048216911684612f5e565b8015612a42576005850154612a4290600160601b90046001600160a01b03167f000000000000000000000000a73685da714cfadba5d23bf38973306d6ff560c783613fbb565b60058501546040516350ecb2f560e01b81526001600160a01b037f000000000000000000000000a73685da714cfadba5d23bf38973306d6ff560c78116926350ecb2f592612aa292600160601b909204909116908b908690600401615510565b600060405180830381600087803b158015612abc57600080fd5b505af1158015612ad0573d6000803e3d6000fd5b5050505050505050505050565b6000818310612aec57816118ad565b5090919050565b610f6784856001600160a01b03166323b872dd868686604051602401612b1b939291906152df565b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506140cb565b612b578133614125565b50565b6002548111158015612b6c5750600081115b612b575760405162461bcd60e51b8152602060048201526016602482015275150e88125b9d985b1a59151bdd5c9b985b595b9d125960521b6044820152606401610d17565b8060c001516001600160401b03168160e001516001600160401b031611612c165760405162461bcd60e51b8152602060048201526019602482015278543a20456e64446174654265666f726553746172744461746560381b6044820152606401610d17565b600081606001516001600160401b031611612c675760405162461bcd60e51b8152602060048201526011602482015270543a20456e7472794c696d69745a65726f60781b6044820152606401610d17565b670de0b6b3a76400008161010001516001600160401b03161115612c9d5760405162461bcd60e51b8152600401610d1790615531565b670de0b6b3a76400008161012001516001600160401b03161115612cd35760405162461bcd60e51b8152600401610d1790615531565b670de0b6b3a76400008161014001516001600160401b03161115612d095760405162461bcd60e51b8152600401610d1790615531565b61022081015115612b57576001546102208201516040516331d98b3f60e01b815260048101919091526000916001600160a01b0316906331d98b3f90602401602060405180830381865afa158015612d65573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d89919061523d565b11612b575760405162461bcd60e51b81526020600482015260176024820152762a1d1024b73b30b634b2283934b1b2a332b2b22830b4b960491b6044820152606401610d17565b60c081015160038401805460e0840151606085015160028801805460a0880151610100808a01516101208b01516001600160401b039a8b166001600160801b031990991698909817600160401b978b168802176001600160801b0316600160801b918b1682026001600160c01b031617600160c01b988b168902179098556101408a015160048d01805460808d0151610100600160481b03600160881b03600160c81b0319909616978c16909302600160881b600160c81b03191696909617600160881b938b169390930292909217600160481b600160881b031916600160481b938a16939093029290921790925561020088015160068b01556101a08801516101608901516101808a0151938916600160401b600160c01b039093169290921790881690950294909417600160401b600160c01b031916600160801b600160c01b031994871690930293909316919091179190931690910217905560405182907f1ca7aac7d8a61e5a730c7b0805c5d3996a1c26239939ac52aeb06703947ee63190612819908490614af1565b6117f583846001600160a01b031663a9059cbb8585604051602401612b1b92919061555c565b7f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680090565b826001600160401b0316600003612fbf5742612fc1565b825b60038301549093506001600160401b0390811690841610801590612ffc575060038201546001600160401b03600160401b9091048116908416105b61303e5760405162461bcd60e51b8152602060048201526013602482015272543a20496e76616c696453746172744461746560681b6044820152606401610d17565b600482015461305d90600160401b90046001600160401b031684615575565b600282018054600160401b600160801b031916600160401b6001600160401b039384168102919091179182905560048501546130a893600160801b9091048116929190910416615575565b600282018054600160801b600160c01b031916600160801b6001600160401b039384168102919091179182905560048501546130f393600160c01b9091048116929190910416615575565b60028201805466ffffffffffffff92909216600160c01b0266ffffffffffffff60c01b19909216919091179055604051849086907f537a110d054f03653f327661270f3d2e697f169fbeae54d58d234d298fab9a7b90600090a35050505050565b60008061315f612f84565b905061316b8484611be5565b6131eb576000848152602082815260408083206001600160a01b03871684529091529020805460ff191660011790556131a13390565b6001600160a01b0316836001600160a01b0316857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a46001915050610d5d565b6000915050610d5d565b600061320085612b5a565b6000858152600660209081526040808320600783528184208885528352818420898552600884528285206001600160a01b0389168087528186528487208b885260018101875294872090875294526002810154919490939291600160401b90046001600160401b031690036132825761327d898960008787612fa8565b6132f9565b600484015460028401546132ae916001600160401b03600160401b918290048116929190910416615594565b6001600160401b03164210156132f95760405162461bcd60e51b815260206004820152601060248201526f543a20526f6f6d4e6f7441637469766560801b6044820152606401610d17565b600283810154908301541580159650600160401b9091046001600160401b0316906000906133cd5750506001840154600284810154908601546003850154600160801b9092046001600160401b039081169392600160881b909204166001600160f81b03909116106133a45760405162461bcd60e51b8152602060048201526014602482015273150e881499589d5e531a5b5a5d14995858da195960621b6044820152606401610d17565b6003840180546001600160f81b03808216600101166001600160f81b03199091161790556134c6565b508454600286015483546101009091046001600160401b0316116134305760405162461bcd60e51b815260206004820152601a602482015279150e88141b185e595c915b9d1c9e531a5b5a5d14995858da195960321b6044820152606401610d17565b600280870154908601546001600160401b03600160481b90920482169116106134965760405162461bcd60e51b8152602060048201526018602482015277150e88149bdbdb515b9d1c9e531a5b5a5d14995858da195960421b6044820152606401610d17565b8254600190810184556002860180546001600160401b031981166001600160401b03918216909301169190911790555b8142106135125760405162461bcd60e51b815260206004820152601a6024820152792a1d102a37bab93730b6b2b73a29ba30b93a32b227b927bb32b960311b6044820152606401610d17565b600286015460ff16156135435780881461353e5760405162461bcd60e51b8152600401610d17906155b3565b613563565b808810156135635760405162461bcd60e51b8152600401610d17906155b3565b505050505050949350505050565b600034156136075760405163918a295360e01b81526001600160a01b037f000000000000000000000000d2e0b028489e24ed832f8f7d95353565d1efbde8169063918a29539034906135cd9088908d908b9030906004016155e2565b6000604051808303818588803b1580156135e657600080fd5b505af11580156135fa573d6000803e3d6000fd5b50505050508390506127a9565b6001600160a01b03871615613751576001600160a01b0380881660009081526009602090815260408083209389168352929052205460ff1661367f5760405162461bcd60e51b8152602060048201526011602482015270150e8814ddd85c139bdd115b98589b1959607a1b6044820152606401610d17565b7f000000000000000000000000d2e0b028489e24ed832f8f7d95353565d1efbde86001600160a01b0316634da9175788878b88600454426136c091906152af565b60005460405160e088901b6001600160e01b03191681526001600160a01b039687166004820152948616602486015292851660448501526064840191909152608483015260a482018b90529190911660c482015260e401600060405180830381600087803b15801561373157600080fd5b505af1158015613745573d6000803e3d6000fd5b505050508390506127a9565b8361375c84846152af565b10156127a9576040516326efbc1960e21b81526000906001600160a01b037f000000000000000000000000d2e0b028489e24ed832f8f7d95353565d1efbde81690639bbef064906137b1908990600401615073565b602060405180830381865afa1580156137ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137f2919061523d565b9050600084613801858861526c565b61380b919061526c565b9050600061381983836153f3565b90506138306001600160a01b0389168c3084612af3565b6138646001600160a01b0389167f000000000000000000000000d2e0b028489e24ed832f8f7d95353565d1efbde883613fbb565b60405163918a295360e01b81526001600160a01b037f000000000000000000000000d2e0b028489e24ed832f8f7d95353565d1efbde8169063918a2953906138b69085908f908d9030906004016155e2565b600060405180830381600087803b1580156138d057600080fd5b505af11580156138e4573d6000803e3d6000fd5b50939d9c50505050505050505050505050565b6020808501516000908152600690915260408120816139168486612add565b90506000613924828761526c565b905081156139d15787516005840154604051638bfb07c960e01b81526001600160a01b037f000000000000000000000000d2e0b028489e24ed832f8f7d95353565d1efbde8811693638bfb07c99361398c939192600160601b909104169087906004016152df565b6020604051808303816000875af11580156139ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139cf919061523d565b505b8015613a91578751600584015460405163d09df7f760e01b81526001600160a01b039283166004820152306024820152600160601b9091048216604482015260648101839052600060848201527f00000000000000000000000087c85a8b87cc703fb4dd32b154896b2fa9e1980d9091169063d09df7f79060a4016020604051808303816000875af1158015613a6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a8f919061523d565b505b6005830154600090613aad9089906001600160601b03166153f3565b90506000613ac58a6000015186848d60200151614154565b8a5160058701546040516307c146e960e01b81526001600160a01b039283166004820152600160601b90910482166024820152336044820152606481018c90529192507f0000000000000000000000009f991d9c34a63af89e8e09befbe2148f66eab16316906307c146e990608401600060405180830381600087803b158015613b4e57600080fd5b505af1158015613b62573d6000803e3d6000fd5b50505060208b015160408c01518c516005890154613bc49450613b8e906001600160601b0316886153f3565b60058a0154613ba6906001600160601b03168a6153f3565b60058b015488908890600160601b90046001600160a01b0316614407565b9a9950505050505050505050565b600080613bdd612f84565b9050613be98484611be5565b156131eb576000848152602082815260408083206001600160a01b0387168085529252808320805460ff1916905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a46001915050610d5d565b600084815260086020908152604080832086516001600160a01b031684528252808320858452600181019092528220805483929190151580613c90575060008160010154115b613cd15760405162461bcd60e51b8152602060048201526012602482015271543a20506c617965724e6f74496e526f6f6d60701b6044820152606401610d17565b6003810154600160f81b900460ff1615613d285760405162461bcd60e51b8152602060048201526018602482015277150e8814185e5bdd5d105b1c9958591e549958d95a5d995960421b6044820152606401610d17565b6003810180546001600160f81b0316600160f81b17905560208701516000908190613d549084896144b7565b600589015460028601549294509092506001600160601b031690613d789084614739565b613d829190615609565b600588015460028501549197506001600160601b031690613da39083614739565b613dad9190615609565b94505050505094509492505050565b8254600090613dcb908361526c565b6001600160a01b0384166000908152600c60205260409020549091508111801590613e0e57506001600160a01b0383166000908152600b60205260409020548111155b15613e7a5780846000016000828254613e2791906152af565b9250508190555080846001016000828254613e4291906152af565b90915550506001600160a01b0383166000908152600b602052604081208054839290613e6f90849061526c565b90915550610f679050565b60405162461bcd60e51b8152602060048201526014602482015273150e88141c9a5e99541bdbdb115e18d95959195960621b6044820152606401610d17565b6000613ed9856001015484613ece919061526c565b6006860154906140bc565b6001860180548583039081019091556005860180546001600160a01b03600160601b9182900481166000908152600a602052604090819020805490950190945560028a0180546001600160f81b0316600160f81b17905591549251632d9f6c6f60e21b81529394507f000000000000000000000000a73685da714cfadba5d23bf38973306d6ff560c782169363b67db1bc93613f82939290049091169086908690600401615510565b600060405180830381600087803b158015613f9c57600080fd5b505af1158015613fb0573d6000803e3d6000fd5b505050505050505050565b6000836001600160a01b031663095ea7b38484604051602401613fdf92919061555c565b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050509050614018848261474e565b610f675761404384856001600160a01b031663095ea7b3866000604051602401612b1b92919061555c565b610f6784826140cb565b60008161406284670de0b6b3a76400006153f3565b6118ad9190615609565b600081600003614087576140808284615609565b9050610d5d565b82156140b3578161409960018561526c565b6140a39190615609565b6140ae9060016152af565b6118ad565b50600092915050565b6000818311612aec57816118ad565b60006140e06001600160a01b038416836147f6565b9050805160001415801561410557508080602001905181019061410391906152c2565b155b156117f55782604051635274afe760e01b8152600401610d179190615073565b61412f8282611be5565b61415057808260405163e2517d3f60e01b8152600401610d1792919061555c565b5050565b60058301546040516370a0823160e01b81526000918291600160601b9091046001600160a01b0316906370a0823190614191903090600401615073565b602060405180830381865afa1580156141ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141d2919061523d565b600354600554604051630dac272b60e21b81526001600160a01b0392831660048201528983166024820152604481018890526064810191909152919250600091829182917f000000000000000000000000699dca792d637dace2ca5f6b2c77382db6e94b7e909116906336b09cac906084016000604051808303816000875af1158015614263573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261428b9190810190615693565b925092509250808410156143355760058801546001600160a01b037f000000000000000000000000a73685da714cfadba5d23bf38973306d6ff560c781169163b67db1bc91600160601b90910416886142e4888661526c565b6040518463ffffffff1660e01b815260040161430293929190615510565b600060405180830381600087803b15801561431c57600080fd5b505af1158015614330573d6000803e3d6000fd5b505050505b60005b83518110156143fa5760006001600160a01b031684828151811061435e5761435e615303565b60200260200101516001600160a01b031614158015614397575082818151811061438a5761438a615303565b6020026020010151600014155b156143f2576143f28482815181106143b1576143b1615303565b60200260200101518483815181106143cb576143cb615303565b602090810291909101015160058c0154600160601b90046001600160a01b03169190612f5e565b600101614338565b5098975050505050505050565b60008881526007602090815260408083208a845282528083208b8452600883528184206001600160a01b038b16855283528184208b8552600101909252822084860392888801906144588288612add565b84548601855560019485018054919093039081019092555060028201805490970190965580549097018755959095018054909401909355506001600160a01b039091166000908152600a60205260409020805490910190559392505050565b60028201548254600091829182906144cf908361404d565b905060006144ea83886001015461404d90919063ffffffff16565b9050670de0b6b3a7640000881115614643576040805161024081018252875481526001808901546020830152600289015460ff81161515938301939093526101008084046001600160401b039081166060850152600160481b850481166080850152600160881b909404841660a084015260038a015480851660c0850152600160401b808204861660e0860152600160801b808304871693860193909352600160c01b91829004861661012086015260048c01548087166101408701529081048616610160860152918204851661018085015290049092166101a082015260058801546001600160601b0381166101c0830152600160601b90046001600160a01b03166101e082015260068801546102008201526007880154610220820152885491890154614639928b9291878661462a89670de0b6b3a764000061526c565b614634919061526c565b614804565b909550935061472e565b8761464e82846152af565b116146e0576004860154600188015460009161467f91869161467991906001600160401b0316614739565b9061404d565b60048801549091506000906146a5906001600160401b0316670de0b6b3a7640000615594565b6001600160401b031690506146ba82856152af565b96506146d785614679838c6001015461473990919063ffffffff16565b9550505061472e565b87821161472a5760006146f3838a61526c565b60048801549091506000906147129083906001600160401b0316614739565b905061471e81856152af565b96506146d7878b61526c565b8794505b505050935093915050565b6000670de0b6b3a764000061406283856153f3565b6000806000846001600160a01b03168460405161476b919061576a565b6000604051808303816000865af19150503d80600081146147a8576040519150601f19603f3d011682016040523d82523d6000602084013e6147ad565b606091505b50915091508180156147d75750805115806147d75750808060200190518101906147d791906152c2565b80156147ed57506000856001600160a01b03163b115b95945050505050565b60606118ad838360006148de565b6000808061481a670de0b6b3a76400008a61526c565b9050600061483f8961010001516001600160401b03168361473990919063ffffffff16565b905060006148648a61012001516001600160401b03168461473990919063ffffffff16565b9050600061488461487d84670de0b6b3a76400006152af565b8b90614739565b905060006148a5838d61014001516001600160401b031661487d91906152af565b90506148b58961467983856152af565b9650876148c2888f61526c565b6148cc919061526c565b95505050505050965096945050505050565b606081471015614903573060405163cd78605960e01b8152600401610d179190615073565b600080856001600160a01b0316848660405161491f919061576a565b60006040518083038185875af1925050503d806000811461495c576040519150601f19603f3d011682016040523d82523d6000602084013e614961565b606091505b509150915061497186838361497b565b9695505050505050565b60608261498b576140ae826149c9565b81511580156149a257506001600160a01b0384163b155b156149c25783604051639996b31560e01b8152600401610d179190615073565b50806118ad565b8051156149d95780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b0381168114612b5757600080fd5b8035614a12816149f2565b919050565b600080600060608486031215614a2c57600080fd5b8335614a37816149f2565b92506020840135614a47816149f2565b929592945050506040919091013590565b600060208284031215614a6a57600080fd5b81356001600160e01b0319811681146118ad57600080fd5b600060208284031215614a9457600080fd5b81356118ad816149f2565b60008060408385031215614ab257600080fd5b8235614abd816149f2565b946020939093013593505050565b600060208284031215614add57600080fd5b5035919050565b6001600160401b03169052565b60006102408201905082518252602083015160208301526040830151614b1b604084018215159052565b506060830151614b2e6060840182614ae4565b506080830151614b416080840182614ae4565b5060a0830151614b5460a0840182614ae4565b5060c0830151614b6760c0840182614ae4565b5060e0830151614b7a60e0840182614ae4565b50610100830151614b8f610100840182614ae4565b50610120830151614ba4610120840182614ae4565b50610140830151614bb9610140840182614ae4565b50610160830151614bce610160840182614ae4565b50610180830151614be3610180840182614ae4565b506101a0830151614bf86101a0840182614ae4565b506101c0830151614c156101c08401826001600160601b03169052565b506101e0830151614c326101e08401826001600160a01b03169052565b5061020083015161020083015261022083015161022083015292915050565b634e487b7160e01b600052604160045260246000fd5b60405161024081016001600160401b0381118282101715614c8a57614c8a614c51565b60405290565b604080519081016001600160401b0381118282101715614c8a57614c8a614c51565b604051601f8201601f191681016001600160401b0381118282101715614cda57614cda614c51565b604052919050565b8015158114612b5757600080fd5b8035614a1281614ce2565b80356001600160401b0381168114614a1257600080fd5b80356001600160601b0381168114614a1257600080fd5b60006102408284031215614d3c57600080fd5b614d44614c67565b82358152602080840135908201529050614d6060408301614cf0565b6040820152614d7160608301614cfb565b6060820152614d8260808301614cfb565b6080820152614d9360a08301614cfb565b60a0820152614da460c08301614cfb565b60c0820152614db560e08301614cfb565b60e0820152614dc76101008301614cfb565b610100820152614dda6101208301614cfb565b610120820152614ded6101408301614cfb565b610140820152614e006101608301614cfb565b610160820152614e136101808301614cfb565b610180820152614e266101a08301614cfb565b6101a0820152614e396101c08301614d12565b6101c0820152614e4c6101e08301614a07565b6101e082015261020082810135908201526102209182013591810191909152919050565b6000806102608385031215614e8457600080fd5b82359150614e958460208501614d29565b90509250929050565b60008060408385031215614eb157600080fd5b8235614ebc816149f2565b91506020830135614ecc816149f2565b809150509250929050565b60008060408385031215614eea57600080fd5b50508035926020909101359150565b600080600060608486031215614f0e57600080fd5b8335614f19816149f2565b92506020840135614f29816149f2565b91506040840135614f3981614ce2565b809150509250925092565b60008083601f840112614f5657600080fd5b5081356001600160401b03811115614f6d57600080fd5b6020830191508360208260051b8501011115614f8857600080fd5b9250929050565b600080600080600060608688031215614fa757600080fd5b8535945060208601356001600160401b03811115614fc457600080fd5b614fd088828901614f44565b90955093505060408601356001600160401b03811115614fef57600080fd5b614ffb88828901614f44565b969995985093965092949392505050565b6000806040838503121561501f57600080fd5b823591506020830135614ecc816149f2565b600060c082840312801561504457600080fd5b509092915050565b60008060006060848603121561506157600080fd5b833592506020840135614a47816149f2565b6001600160a01b0391909116815260200190565b60008060006060848603121561509c57600080fd5b83359250602084013591506150b360408501614cfb565b90509250925092565b60006001600160401b038211156150d5576150d5614c51565b5060051b60200190565b6000806000606084860312156150f457600080fd5b833592506020840135915060408401356001600160401b0381111561511857600080fd5b8401601f8101861361512957600080fd5b803561513c615137826150bc565b614cb2565b8082825260208201915060208360061b85010192508883111561515e57600080fd5b6020840193505b828410156151af576040848a03121561517d57600080fd5b615185614c90565b8435615190816149f2565b8152602085810135818301529083526040909401939190910190615165565b809450505050509250925092565b600061024082840312156151d057600080fd5b6118ad8383614d29565b6000602082840312156151ec57600080fd5b81356118ad81614ce2565b60008060006060848603121561520c57600080fd5b505081359360208301359350604090920135919050565b6001600160a01b0392831681529116602082015260400190565b60006020828403121561524f57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b81810381811115610d5d57610d5d615256565b602080825260169082015275543a20496e73756666696369656e7442616c616e636560501b604082015260600190565b80820180821115610d5d57610d5d615256565b6000602082840312156152d457600080fd5b81516118ad81614ce2565b6001600160a01b039384168152919092166020820152604081019190915260600190565b634e487b7160e01b600052603260045260246000fd5b602080825260169082015275543a20526f6f6d20616c72656164792061637469766560501b604082015260600190565b60006020828403121561535b57600080fd5b6118ad82614cfb565b600060c082840312801561537757600080fd5b5060405160009060c081016001600160401b038111828210171561539d5761539d614c51565b60405283356153ab816149f2565b8152602084810135908201526040808501359082015260608401356153cf816149f2565b60608201526080848101359082015260a09384013593810193909352509092915050565b8082028115828204841417610d5d57610d5d615256565b6001600160a01b0384168152606060208083018290528451918301829052600091908501906080840190835b8181101561545d5783516001600160a01b0316835260209384019390920191600101615436565b505083810360408501528451808252602091820192509085019060005b8181101561549857825184526020938401939092019160010161547a565b5091979650505050505050565b6000606082018583528460208401526060604084015280845180835260808501915060208601925060005b8181101561550357835180516001600160a01b0316845260209081015181850152909301926040909201916001016154d0565b5090979650505050505050565b6001600160a01b039390931683526020830191909152604082015260600190565b602080825260119082015270150e88125b9d985b1a5914195c98d95b9d607a1b604082015260600190565b6001600160a01b03929092168252602082015260400190565b6001600160401b038181168382160190811115610d5d57610d5d615256565b6001600160401b038281168282160390811115610d5d57610d5d615256565b602080825260159082015274150e88125b9d985b1a59115b9d1c9e505b5bdd5b9d605a1b604082015260600190565b9384526001600160a01b039283166020850152908216604084015216606082015260800190565b60008261562657634e487b7160e01b600052601260045260246000fd5b500490565b600082601f83011261563c57600080fd5b815161564a615137826150bc565b8082825260208201915060208360051b86010192508583111561566c57600080fd5b602085015b83811015615689578051835260209283019201615671565b5095945050505050565b6000806000606084860312156156a857600080fd5b83516001600160401b038111156156be57600080fd5b8401601f810186136156cf57600080fd5b80516156dd615137826150bc565b8082825260208201915060208360051b8501019250888311156156ff57600080fd5b6020840193505b8284101561572a578351615719816149f2565b825260209384019390910190615706565b6020880151909650925050506001600160401b0381111561574a57600080fd5b6157568682870161562b565b604095909501519396949550929392505050565b6000825160005b8181101561578b5760208186018101518583015201615771565b50600092019182525091905056fee2b7fb3b832174769106daebcfd6d1970523240dda11281102db9363b83b0dc4a49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775a2646970667358221220b103b14044fec890ce2f026e73a0752823755a8d962612203f13ea42b0d8c09564736f6c634300081b0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000087c85a8b87cc703fb4dd32b154896b2fa9e1980d000000000000000000000000d2e0b028489e24ed832f8f7d95353565d1efbde80000000000000000000000000ae1a1eb1950b037cccab33218e44e8d99fd72e80000000000000000000000009f991d9c34a63af89e8e09befbe2148f66eab163000000000000000000000000a73685da714cfadba5d23bf38973306d6ff560c7000000000000000000000000699dca792d637dace2ca5f6b2c77382db6e94b7e0000000000000000000000008b42bab5cfe04931b433f105338582e0a07dc26d
-----Decoded View---------------
Arg [0] : _tickets (address): 0x87c85a8b87Cc703fB4Dd32B154896B2fa9e1980D
Arg [1] : _credits (address): 0xD2e0b028489E24ED832f8f7D95353565d1eFbdE8
Arg [2] : _playerCard (address): 0x0aE1A1eb1950B037CCcAB33218e44E8d99Fd72E8
Arg [3] : _playthroughTracker (address): 0x9F991d9c34a63aF89e8E09BEfbe2148f66EAB163
Arg [4] : _bonusCash (address): 0xA73685da714cFADbA5d23bF38973306D6ff560c7
Arg [5] : _feeSplitter (address): 0x699DCA792d637DACE2ca5F6b2C77382DB6e94B7E
Arg [6] : _tokenRegistry (address): 0x8b42BaB5CFE04931b433f105338582e0a07Dc26d
-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 00000000000000000000000087c85a8b87cc703fb4dd32b154896b2fa9e1980d
Arg [1] : 000000000000000000000000d2e0b028489e24ed832f8f7d95353565d1efbde8
Arg [2] : 0000000000000000000000000ae1a1eb1950b037cccab33218e44e8d99fd72e8
Arg [3] : 0000000000000000000000009f991d9c34a63af89e8e09befbe2148f66eab163
Arg [4] : 000000000000000000000000a73685da714cfadba5d23bf38973306d6ff560c7
Arg [5] : 000000000000000000000000699dca792d637dace2ca5f6b2c77382db6e94b7e
Arg [6] : 0000000000000000000000008b42bab5cfe04931b433f105338582e0a07dc26d
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
[ Download: CSV Export ]
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.