ETH Price: $3,411.40 (-7.22%)
 

Overview

ETH Balance

0.0064571 ETH

Eth Value

$22.03 (@ $3,411.40/ETH)

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Manual Daily Dif...213960762024-12-13 20:47:4725 days ago1734122867IN
Legacy: LEGACY Token
0 ETH0.001784316.66325089
Approve213824322024-12-11 23:05:4727 days ago1733958347IN
Legacy: LEGACY Token
0 ETH0.0012154826.17266936
Approve213780582024-12-11 8:24:2327 days ago1733905463IN
Legacy: LEGACY Token
0 ETH0.0007530116.13109227
Manual Daily Dif...212671792024-11-25 20:33:3543 days ago1732566815IN
Legacy: LEGACY Token
0 ETH0.0028388921.78433233
Approve211751122024-11-13 0:13:2356 days ago1731456803IN
Legacy: LEGACY Token
0 ETH0.0009831421.1751998
Approve211430522024-11-08 12:54:3560 days ago1731070475IN
Legacy: LEGACY Token
0 ETH0.0010040921.52631207
Manual Daily Dif...210939402024-11-01 16:17:4767 days ago1730477867IN
Legacy: LEGACY Token
0 ETH0.0007260714.82233229
Manual Daily Dif...210713062024-10-29 12:31:3570 days ago1730205095IN
Legacy: LEGACY Token
0 ETH0.0005105412.38025091
Approve210690222024-10-29 4:51:3570 days ago1730177495IN
Legacy: LEGACY Token
0 ETH0.0004866210.42448536
Manual Daily Dif...210663772024-10-28 19:59:5971 days ago1730145599IN
Legacy: LEGACY Token
0 ETH0.0006102514.79805384
Approve210660032024-10-28 18:45:1171 days ago1730141111IN
Legacy: LEGACY Token
0 ETH0.0007327915.71820909
Approve210641022024-10-28 12:22:2371 days ago1730118143IN
Legacy: LEGACY Token
0 ETH0.0005393511.56903176
Approve210595012024-10-27 20:57:2372 days ago1730062643IN
Legacy: LEGACY Token
0 ETH0.0010143121.72871682
Approve210594962024-10-27 20:56:2372 days ago1730062583IN
Legacy: LEGACY Token
0 ETH0.000390018.35486422
Approve210594902024-10-27 20:55:1172 days ago1730062511IN
Legacy: LEGACY Token
0 ETH0.0004701510.07159129
Approve210594892024-10-27 20:54:5972 days ago1730062499IN
Legacy: LEGACY Token
0 ETH0.000375138.03614785
Approve210594892024-10-27 20:54:5972 days ago1730062499IN
Legacy: LEGACY Token
0 ETH0.0014021130.03614785
Approve210594882024-10-27 20:54:4772 days ago1730062487IN
Legacy: LEGACY Token
0 ETH0.0011182723.96181543
Approve210584462024-10-27 17:25:5972 days ago1730049959IN
Legacy: LEGACY Token
0 ETH0.000392588.47303774
Approve210583952024-10-27 17:15:4772 days ago1730049347IN
Legacy: LEGACY Token
0 ETH0.0005381611.53160494
Manual Daily Dif...210572522024-10-27 13:26:1172 days ago1730035571IN
Legacy: LEGACY Token
0 ETH0.0003678.89954638
Approve210555092024-10-27 7:36:1172 days ago1730014571IN
Legacy: LEGACY Token
0 ETH0.000324296.95588087
Approve210542122024-10-27 3:15:3572 days ago1729998935IN
Legacy: LEGACY Token
0 ETH0.000290726.22783217
Approve210542102024-10-27 3:15:1172 days ago1729998911IN
Legacy: LEGACY Token
0 ETH0.000425149.107371
Approve210541662024-10-27 3:06:2372 days ago1729998383IN
Legacy: LEGACY Token
0 ETH0.000191744.12988703
View all transactions

Latest 4 internal transactions

Advanced mode:
Parent Transaction Hash Block
From
To
210496112024-10-26 11:51:1173 days ago1729943471
Legacy: LEGACY Token
0.0009934 ETH
210496112024-10-26 11:51:1173 days ago1729943471
Legacy: LEGACY Token
0.00208614 ETH
210496112024-10-26 11:51:1173 days ago1729943471
Legacy: LEGACY Token
0.00039736 ETH
210496112024-10-26 11:51:1173 days ago1729943471
Legacy: LEGACY Token
0.000066 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Legacy

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 16 : Legacy.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import "../interfaces/ILegacyOnBurn.sol";
import "../interfaces/ILEGACY.sol";

import "../libs/calcFunctions.sol";

import "./GlobalInfo.sol";
import "./MintInfo.sol";
import "./OwnerInfo.sol";
import "./StakeInfo.sol";

//custom errors
	error Legacy_InvalidAmount();
	error Legacy_InsufficientBalance();
	error Legacy_NotSupportedContract();
	error Legacy_InsufficientProtocolFees();
	error Legacy_FailedToSendAmount();
	error Legacy_NotAllowed();
	error Legacy_NoCycleRewardToClaim();
	error Legacy_NoSharesExist();
	error Legacy_EmptyUndistributeFees();
	error Legacy_InvalidBatchCount();
	error Legacy_MaxedWalletMints();
	error Legacy_LPTokensHasMinted();
	error Legacy_InvalidAddress();
	error Legacy_MintingPhaseFinished();

/** @title Legacy */
contract Legacy is ERC20, ReentrancyGuard, GlobalInfo, MintInfo, StakeInfo, OwnerInfo {
	/** Storage Variables*/
	/** @dev stores genesis wallet address */
	address private s_genesisAddress;
	/** @dev stores buy and burn contract address */
	address private s_buyAndBurnAddress;
	/** @dev stores titanX buy and burn contract address */
	address private s_titanXbuyAndBurnAddress;
	/** @dev tracks total protocol fees sent to TitanX Buy&Burn */
	uint256 private s_totalTitanxDistributedBurnFees;
	/** @dev tracks collected protocol fees until it is distributed */
	uint88 private s_undistributedFees;
	/** @dev tracks if initial LP tokens has minted or not */
	InitialLPMinted private s_initialLPMinted;

	event ProtocolFeeReceived(address indexed user, uint256 indexed day, uint256 indexed amount);
	event ETHDistributed(address indexed caller, uint256 indexed amount);
	event CyclePayoutTriggered(
		address indexed caller,
		uint256 indexed cycleNo,
		uint256 indexed reward
	);
	event RewardClaimed(address indexed user, uint256 indexed reward);

	constructor(address genesisAddress) ERC20("Legacy", "LEGACY") {
		if (genesisAddress == address(0)) revert Legacy_InvalidAddress();
		s_genesisAddress = genesisAddress;
	}
	// all eth transfers should be added to s_undistributedFees
	receive() external payable {
		s_undistributedFees += uint88(msg.value);
		emit ProtocolFeeReceived(msg.sender, getCurrentContractDay(), msg.value);
	}

	/**** Mint Functions *****/
	/** @notice create a new mint
     * @param mintPower 1 - 100
     * @param numOfDays mint length of 1 - 280
     */
	function startMint(
		uint256 mintPower,
		uint256 numOfDays
	) external payable nonReentrant dailyDifficultyClock {
		if (getUserLatestMintId(_msgSender()) + 1 > MAX_MINT_PER_WALLET)
			revert Legacy_MaxedWalletMints();
		if (getCurrentMintableLegacy() <= MINTABLE_LEGACY_END)
			revert Legacy_MintingPhaseFinished();
		uint256 gMintPower = getGlobalMintPower() + mintPower;
		uint256 currentLRank = getGlobalLRank() + 1;
		uint256 gMinting = getTotalMinting() +
		_startMint(
			_msgSender(),
			mintPower,
			numOfDays,
			getCurrentMintableLegacy(),
			getCurrentMintPowerBonus(),
			getCurrentEAABonus(),
			gMintPower,
			currentLRank,
			getBatchMintCost(mintPower, 1, getCurrentMintCost())
		);
		_updateMintStats(currentLRank, gMintPower, gMinting);
		_protocolFees(mintPower, 1);
	}

	/** @notice create new mints in batch of up to 100 mints
     * @param mintPower 1 - 100
     * @param numOfDays mint length of 1 - 280
     * @param count 1 - 100
     */
	function batchMint(
		uint256 mintPower,
		uint256 numOfDays,
		uint256 count
	) external payable nonReentrant dailyDifficultyClock {
		if (count == 0 || count > MAX_BATCH_MINT_COUNT) revert Legacy_InvalidBatchCount();
		if (getUserLatestMintId(_msgSender()) + count > MAX_MINT_PER_WALLET)
			revert Legacy_MaxedWalletMints();
		if (getCurrentMintableLegacy() <= MINTABLE_LEGACY_END)
			revert Legacy_MintingPhaseFinished();

		_startBatchMint(
			_msgSender(),
			mintPower,
			numOfDays,
			getCurrentMintableLegacy(),
			getCurrentMintPowerBonus(),
			getCurrentEAABonus(),
			count,
			getBatchMintCost(mintPower, 1, getCurrentMintCost()) //only need 1 mint cost for all mints
		);
		_protocolFees(mintPower, count);
	}

	/** @notice claim a matured mint
     * @param id mint id
     */
	function claimMint(uint256 id) external dailyDifficultyClock nonReentrant {
		_mintReward(_claimMint(_msgSender(), id, MintAction.CLAIM));
	}

	/** @notice batch claim matured mint of up to 100 claims per run
     */
	function batchClaimMint() external dailyDifficultyClock nonReentrant {
		_mintReward(_batchClaimMint(_msgSender()));
	}

	/**** Stake Functions *****/
	/** @notice start a new stake
     * @param amount Legacy amount
     * @param numOfDays stake length
     */
	function startStake(uint256 amount, uint256 numOfDays) external dailyDifficultyClock nonReentrant {
		if (balanceOf(_msgSender()) < amount) revert Legacy_InsufficientBalance();

		_burn(_msgSender(), amount);
		_initFirstSharesCycleIndex(
			_msgSender(),
			_startStake(
				_msgSender(),
				amount,
				numOfDays,
				getCurrentShareRate(),
				getCurrentContractDay(),
				getGlobalPayoutTriggered()
			)
		);
	}

	/** @notice end a stake
     * @param id stake id
     */
	function endStake(uint256 id) external dailyDifficultyClock nonReentrant {
		_mint(
			_msgSender(),
			_endStake(
				_msgSender(),
				id,
				getCurrentContractDay(),
				StakeAction.END,
				getGlobalPayoutTriggered()
			)
		);
	}

	/** @notice distribute the collected protocol fees into different pools/payouts
     * automatically send the incentive fee to caller, buyAndBurnFunds to BuyAndBurn contract,
     * titanXbuyAndBurnFunds to TitanX BuyAndBurn contract, and genesis wallet
     */
	function distributeETH() external dailyDifficultyClock nonReentrant {
		(uint256 incentiveFee, uint256 buyAndBurnFunds, uint256 titanXbuyAndBurnFunds, uint256 genesisWallet) = _distributeETH();
		_sendFunds(incentiveFee, buyAndBurnFunds, titanXbuyAndBurnFunds, genesisWallet);
	}

	/** @notice trigger cycle payouts for day 28, 90, 369, 888
     * As long as the cycle has met its maturity day (eg. Cycle28 is day 28), payout can be triggered in any day onwards
     */
	function triggerPayouts() external dailyDifficultyClock nonReentrant {
		uint256 globalActiveShares = getGlobalShares() - getGlobalExpiredShares();
		if (globalActiveShares < 1) revert Legacy_NoSharesExist();

		uint256 incentiveFee;
		uint256 buyAndBurnFunds;
		uint256 titanXbuyAndBurnFunds;
		uint256 genesisWallet;
		if (s_undistributedFees != 0)
			(incentiveFee, buyAndBurnFunds, titanXbuyAndBurnFunds, genesisWallet) = _distributeETH();

		uint256 currentContractDay = getCurrentContractDay();
		PayoutTriggered isTriggered = PayoutTriggered.NO;
		_triggerCyclePayout(DAY28, globalActiveShares, currentContractDay) == PayoutTriggered.YES &&
		isTriggered == PayoutTriggered.NO
		? isTriggered = PayoutTriggered.YES
		: isTriggered;
		_triggerCyclePayout(DAY90, globalActiveShares, currentContractDay) == PayoutTriggered.YES &&
		isTriggered == PayoutTriggered.NO
		? isTriggered = PayoutTriggered.YES
		: isTriggered;
		_triggerCyclePayout(DAY369, globalActiveShares, currentContractDay) ==
		PayoutTriggered.YES &&
		isTriggered == PayoutTriggered.NO
		? isTriggered = PayoutTriggered.YES
		: isTriggered;
		_triggerCyclePayout(DAY888, globalActiveShares, currentContractDay) ==
		PayoutTriggered.YES &&
		isTriggered == PayoutTriggered.NO
		? isTriggered = PayoutTriggered.YES
		: isTriggered;

		if (isTriggered == PayoutTriggered.YES) {
			if (getGlobalPayoutTriggered() == PayoutTriggered.NO) _setGlobalPayoutTriggered();
		}

		if (incentiveFee != 0) _sendFunds(incentiveFee, buyAndBurnFunds, titanXbuyAndBurnFunds, genesisWallet);
	}

	/** @notice claim all user available ETH payouts in one call */
	function claimUserAvailableETHPayouts() external dailyDifficultyClock nonReentrant {
		uint256 reward = _claimCyclePayout(DAY28, PayoutClaim.SHARES);
		reward += _claimCyclePayout(DAY90, PayoutClaim.SHARES);
		reward += _claimCyclePayout(DAY369, PayoutClaim.SHARES);
		reward += _claimCyclePayout(DAY888, PayoutClaim.SHARES);

		if (reward == 0) revert Legacy_NoCycleRewardToClaim();
		_sendViaCall(payable(_msgSender()), reward);
		emit RewardClaimed(_msgSender(), reward);
	}

	/** @notice Set BuyAndBurn Contract Address - able to change to new contract that supports UniswapV4+
     * Only owner can call this function
     * @param contractAddress BuyAndBurn contract address
     */
	function setBuyAndBurnContractAddress(address contractAddress) external onlyOwner {
		if (contractAddress == address(0)) revert Legacy_InvalidAddress();
		s_buyAndBurnAddress = contractAddress;
	}

	/** @notice Set TitanX BuyAndBurn Contract Address
     * Only owner can call this function
     * @param contractAddress TitanX BuyAndBurn contract address
     */
	function setTitanXBuyAndBurnContractAddress(address contractAddress) external onlyOwner {
		if (contractAddress == address(0)) revert Legacy_InvalidAddress();
		s_titanXbuyAndBurnAddress = contractAddress;
	}

	/** @notice Set to new genesis wallet. Only genesis wallet can call this function
     * @param newAddress new genesis wallet address
     */
	function setNewGenesisAddress(address newAddress) external {
		if (_msgSender() != s_genesisAddress) revert Legacy_NotAllowed();
		if (newAddress == address(0)) revert Legacy_InvalidAddress();
		s_genesisAddress = newAddress;
	}

	/** @notice mint initial LP tokens. Only BuyAndBurn contract set by genesis wallet can call this function
     */
	function mintLPTokens() external {
		if (_msgSender() != s_buyAndBurnAddress) revert Legacy_NotAllowed();
		if (s_initialLPMinted == InitialLPMinted.YES) revert Legacy_LPTokensHasMinted();
		s_initialLPMinted = InitialLPMinted.YES;
		_mint(s_buyAndBurnAddress, INITAL_LP_TOKENS);
	}

	/** @notice burn all BuyAndBurn contract Legacy */
	function burnLPTokens() external dailyDifficultyClock {
		_burn(s_buyAndBurnAddress, balanceOf(s_buyAndBurnAddress));
	}

	//private functions
	/** @dev mint reward to user and 5% to genesis wallet
     * @param reward Legacy amount
     */
	function _mintReward(uint256 reward) private {
		_mint(_msgSender(), reward);
		_mint(s_genesisAddress, (reward * 500) / PERCENT_BPS);
	}

	/** @dev send native currency to respective parties
     * @param incentiveFee fees for caller to run distributeETH()
     * @param buyAndBurnFunds funds for buy and burn
     * @param titanXbuyAndBurnFunds funds for buy and burn of TitanX protocol
     * @param genesisWalletFunds funds for genesis wallet
     */
	function _sendFunds(
		uint256 incentiveFee,
		uint256 buyAndBurnFunds,
		uint256 titanXbuyAndBurnFunds,
		uint256 genesisWalletFunds
	) private {
		_sendViaCall(payable(_msgSender()), incentiveFee);
		_sendViaCall(payable(s_genesisAddress), genesisWalletFunds);
		if (getCurrentMintableLegacy() > MINTABLE_LEGACY_END) {
			_sendViaCall(payable(s_buyAndBurnAddress), buyAndBurnFunds);
			// Tracks Fees sent to TitanX Buy & Burn
			s_totalTitanxDistributedBurnFees += titanXbuyAndBurnFunds;
			_sendViaCall(payable(s_titanXbuyAndBurnAddress), titanXbuyAndBurnFunds);
		}
	}

	/** @dev calculation to distribute collected protocol fees into different pools/parties */
	function _distributeETH()
	private
	returns (uint256 incentiveFee, uint256 buyAndBurnFunds, uint256 titanXbuyAndBurnFunds, uint256 genesisWallet)
	{
		uint256 accumulatedFees = s_undistributedFees;
		if (accumulatedFees == 0) revert Legacy_EmptyUndistributeFees();
		s_undistributedFees = 0;
		emit ETHDistributed(_msgSender(), accumulatedFees);

		incentiveFee = (accumulatedFees * INCENTIVE_FEE_PERCENT) / INCENTIVE_FEE_PERCENT_BASE; //0.66%
		accumulatedFees -= incentiveFee;

		// we should not be calculating burning fees when builder phase is over
		buyAndBurnFunds = 0;
		titanXbuyAndBurnFunds = 0;
		// accumulate burn funds during builder phase
		if (getCurrentMintableLegacy() > MINTABLE_LEGACY_END) {
			buyAndBurnFunds = (accumulatedFees * PERCENT_TO_BUY_AND_BURN) / PERCENT_BPS;
			titanXbuyAndBurnFunds = (accumulatedFees * TITANX_PERCENT_TO_BUY_AND_BURN) / PERCENT_BPS;
		}

		genesisWallet = (accumulatedFees * PERCENT_TO_GENESIS) / PERCENT_BPS;
		uint256 cycleRewardPool = accumulatedFees -
		buyAndBurnFunds - titanXbuyAndBurnFunds -
		genesisWallet;

		//cycle payout
		if (cycleRewardPool != 0) {
			uint256 cycle28Reward = (cycleRewardPool * CYCLE_28_PERCENT) / PERCENT_BPS;
			uint256 cycle90Reward = (cycleRewardPool * CYCLE_90_PERCENT) / PERCENT_BPS;
			uint256 cycle369Reward = (cycleRewardPool * CYCLE_369_PERCENT) / PERCENT_BPS;
			_setCyclePayoutPool(DAY28, cycle28Reward);
			_setCyclePayoutPool(DAY90, cycle90Reward);
			_setCyclePayoutPool(DAY369, cycle369Reward);
			_setCyclePayoutPool(
				DAY888,
				cycleRewardPool - cycle28Reward - cycle90Reward - cycle369Reward
			);
		}
	}

	/** @dev calculate required protocol fees, and return the balance (if any)
     * @param mintPower mint power 1-100
     * @param count how many mints
     */
	function _protocolFees(uint256 mintPower, uint256 count) private {
		uint256 protocolFee;

		protocolFee = getBatchMintCost(mintPower, count, getCurrentMintCost());
		if (msg.value < protocolFee) revert Legacy_InsufficientProtocolFees();

		uint256 feeBalance;
		s_undistributedFees += uint88(protocolFee);
		feeBalance = msg.value - protocolFee;

		if (feeBalance != 0) {
			_sendViaCall(payable(_msgSender()), feeBalance);
		}

		emit ProtocolFeeReceived(_msgSender(), getCurrentContractDay(), protocolFee);
	}

	/** @dev calculate payouts for each cycle day tracked by cycle index
     * @param cycleNo cycle day 28, 90, 369, 888
     * @param currentContractDay current contract day
     * @return triggered is payout triggered successfully
     */
	function _triggerCyclePayout(
		uint256 cycleNo,
		uint256 globalActiveShares,
		uint256 currentContractDay
	) private returns (PayoutTriggered triggered) {
		//check against cycle payout maturity day
		if (currentContractDay < getNextCyclePayoutDay(cycleNo)) return PayoutTriggered.NO;

		//update the next cycle payout day regardless of payout triggered successfully or not
		_setNextCyclePayoutDay(cycleNo);

		uint256 reward = getCyclePayoutPool(cycleNo);
		if (reward == 0) return PayoutTriggered.NO;

		//calculate cycle reward per share
		_calculateCycleRewardPerShare(cycleNo, reward, globalActiveShares);

		emit CyclePayoutTriggered(_msgSender(), cycleNo, reward);

		return PayoutTriggered.YES;
	}

	/** @dev calculate user reward with specified cycle day and claim type (shares) and update user's last claim cycle index
     * @param cycleNo cycle day 8, 28, 90, 369, 888
     * @param payoutClaim claim type - (Shares=0)
     */
	function _claimCyclePayout(uint256 cycleNo, PayoutClaim payoutClaim) private returns (uint256) {
		(
		uint256 reward,
		uint256 userClaimCycleIndex,
		uint256 userClaimSharesIndex
		) = _calculateUserCycleReward(_msgSender(), cycleNo, payoutClaim);

		if (payoutClaim == PayoutClaim.SHARES)
			_updateUserClaimIndexes(
				_msgSender(),
				cycleNo,
				userClaimCycleIndex,
				userClaimSharesIndex
			);

		return reward;
	}

	/** @dev Recommended method to use to send native coins.
     * @param to receiving address.
     * @param amount in wei.
     */
	function _sendViaCall(address payable to, uint256 amount) private {
		if (to == address(0)) revert Legacy_InvalidAddress();
		(bool sent, ) = to.call{value: amount}("");
		if (!sent) revert Legacy_FailedToSendAmount();
	}

	//Views
	/** @dev calculate user payout reward with specified cycle day and claim type (shares).
     * it loops through all the unclaimed cycle index until the latest cycle index
     * @param user user address
     * @param cycleNo cycle day 28, 90, 369, 888
     * @param payoutClaim claim type (Shares=0)
     * @return rewards calculated reward
     * @return userClaimCycleIndex last claim cycle index
     * @return userClaimSharesIndex last claim shares index
     */
	function _calculateUserCycleReward(
		address user,
		uint256 cycleNo,
		PayoutClaim payoutClaim
	)
	private
	view
	returns (
		uint256 rewards,
		uint256 userClaimCycleIndex,
		uint256 userClaimSharesIndex
	)
	{
		uint256 cycleMaxIndex = getCurrentCycleIndex(cycleNo);

		if (payoutClaim == PayoutClaim.SHARES) {
			(userClaimCycleIndex, userClaimSharesIndex) = getUserLastClaimIndex(user, cycleNo);
			uint256 sharesMaxIndex = getUserLatestShareIndex(user);

			for (uint256 i = userClaimCycleIndex; i <= cycleMaxIndex; i++) {
				(uint256 payoutPerShare, uint256 payoutDay) = getPayoutPerShare(cycleNo, i);
				uint256 shares;

				//loop shares indexes to find the last updated shares before/same triggered payout day
				for (uint256 j = userClaimSharesIndex; j <= sharesMaxIndex; j++) {
					if (getUserActiveSharesDay(user, j) <= payoutDay)
						shares = getUserActiveShares(user, j);
					else break;

					userClaimSharesIndex = j;
				}

				if (payoutPerShare != 0 && shares != 0) {
					//reward has 18 decimals scaling, so here divide by 1e18
					rewards += (shares * payoutPerShare) / SCALING_FACTOR_1e18;
				}

				userClaimCycleIndex = i + 1;
			}
		}
	}

	/** @notice get contract ETH balance
     * @return balance eth balance
     */
	function getBalance() public view returns (uint256) {
		return address(this).balance;
	}

	/** @notice get total fees sent to TitanX Buy & Burn
     * @return amount native currency amount
     */
	function getTitanxDistributedBurnFees() public view returns (uint256) {
		return s_totalTitanxDistributedBurnFees;
	}

	/** @notice get undistributed Fees balance
     * @return amount native currency amount
     */
	function getUndistributedEth() public view returns (uint256) {
		return s_undistributedFees;
	}

	/** @notice get user ETH payout for all cycles
     * @param user user address
     * @return reward total reward
     */
	function getUserETHClaimableTotal(address user) public view returns (uint256 reward) {
		uint256 _reward;
		(_reward, , ) = _calculateUserCycleReward(user, DAY28, PayoutClaim.SHARES);
		reward += _reward;
		(_reward, , ) = _calculateUserCycleReward(user, DAY90, PayoutClaim.SHARES);
		reward += _reward;
		(_reward, , ) = _calculateUserCycleReward(user, DAY369, PayoutClaim.SHARES);
		reward += _reward;
		(_reward, , ) = _calculateUserCycleReward(user, DAY888, PayoutClaim.SHARES);
		reward += _reward;
	}

	/** @notice get total penalties from mint and stake
     * @return amount total penalties
     */
	function getTotalPenalties() public view returns (uint256) {
		return getTotalMintPenalty() + getTotalStakePenalty();
	}

	/** @notice Public function to return the total minted legacy from MintInfo
     * @return total amount of Legacy minted
     */
	function getTotalLegacyRewardsMinted() public view returns (uint256) {
		return getTotalMintedLegacy();
	}


	/** @notice Return the total minted legacy from MintInfo
     * @return total amount of Legacy minted
     */
	function _getTotalMintedLegacy() internal view override returns (uint256) {
		return getTotalMintedLegacy();
	}

	//Public function for updating difficulty clock
	/** @notice allow anyone to sync dailyDifficultyClock manually */
	function manualDailyDifficultyClock() public dailyDifficultyClock {}

}

File 2 of 16 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }
}

File 3 of 16 : ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(address from, address to, uint256 amount) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _totalSupply -= amount;
        }

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}

File 4 of 16 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 5 of 16 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @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 amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

File 6 of 16 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

File 7 of 16 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @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);
}

File 8 of 16 : GlobalInfo.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "../libs/enum.sol";
import "../libs/constant.sol";

abstract contract GlobalInfo {
    //Variables
    //deployed timestamp
    uint256 private immutable i_genesisTs;

    /** @dev track current contract day */
    uint256 private s_currentContractDay;
    /** @dev shareRate starts 800 ether and increases capped at 2800 ether, uint72 has enough size */
    uint72 private s_currentshareRate;
    /** @dev mintCost starts 0.2 ether increases and capped at 1 ether, uint64 has enough size */
    uint64 private s_currentMintCost;
    /** @dev mintableLegacy starts 100 ether decreases to 0 ether, uint96 has enough size */
    uint96 private s_currentMintableLegacy;
    /** @dev mintPowerBonus starts 3_000_000 and decreases capped at 3_000, uint32 has enough size */
    uint32 private s_currentMintPowerBonus;
    /** @dev EAABonus starts 10_000_000 and decreases to 0, uint32 has enough size */
    uint32 private s_currentEAABonus;

    /** @dev track if any of the cycle day 28, 90, 369, 888 has payout triggered successfully
     * this is used in end stake where either the shares change should be tracked in current/next payout cycle
     */
    PayoutTriggered private s_isGlobalPayoutTriggered;

    /** @dev track payouts based on every cycle day 28, 90, 369, 888 when distributeETH() is called */
    mapping(uint256 => uint256) private s_cyclePayouts;

    /** @dev track payout index for each cycle day, increased by 1 when triggerPayouts() is called successfully
     *  eg. current index is 2, s_cyclePayoutIndex[DAY28] = 2 */
    mapping(uint256 => uint256) private s_cyclePayoutIndex;

    /** @dev track payout info (day and payout per share) for each cycle day
     * eg. s_cyclePayoutIndex is 2,
     *  s_CyclePayoutPerShare[DAY28][2].day = 28
     * s_CyclePayoutPerShare[DAY28][2].payoutPerShare = 0.1
     */
    mapping(uint256 => mapping(uint256 => CycleRewardPerShare)) private s_cyclePayoutPerShare;

    /** @dev track user last payout reward claim index for cycleIndex and sharesIndex
     * so calculation would start from next index instead of the first index
     * [address][DAY28].cycleIndex = 1
     * [address][DAY28].sharesIndex = 2
     * cycleIndex is the last stop in s_cyclePayoutPerShare
     * sharesIndex is the last stop in s_addressIdToActiveShares
     */
    mapping(address => mapping(uint256 => UserCycleClaimIndex))
    private s_addressCycleToLastClaimIndex;

    /** @dev track when is the next cycle payout day for each cycle day
     * eg. s_nextCyclePayoutDay[DAY28] = 28
     *     s_nextCyclePayoutDay[DAY90] = 90
     */
    mapping(uint256 => uint256) s_nextCyclePayoutDay;

    //structs
    struct CycleRewardPerShare {
        uint256 day;
        uint256 payoutPerShare;
    }

    struct UserCycleClaimIndex {
        uint96 cycleIndex;
        uint64 sharesIndex;
    }

    //event
    event GlobalDailyDifficultyClockStats(
        uint256 indexed day,
        uint256 indexed mintCost,
        uint256 indexed shareRate,
        uint256 mintableLegacy,
        uint256 mintPowerBonus,
        uint256 EAABonus
    );

    /** @dev Updates variables in terms of day, used in all external/public functions (excluding view)
     */
    modifier dailyDifficultyClock() {
        _dailyDifficultyClock();
        _;
    }

    constructor() {
        i_genesisTs = block.timestamp;
        s_currentContractDay = 1;
        s_currentMintCost = uint64(START_MAX_MINT_COST);
        s_currentMintableLegacy = uint96(START_MAX_MINTABLE_PER_DAY);
        s_currentshareRate = uint72(START_SHARE_RATE);
        s_currentMintPowerBonus = uint32(START_MINTPOWER_INCREASE_BONUS);
        s_currentEAABonus = uint32(EAA_START);
        s_nextCyclePayoutDay[DAY28] = DAY28;
        s_nextCyclePayoutDay[DAY90] = DAY90;
        s_nextCyclePayoutDay[DAY369] = DAY369;
        s_nextCyclePayoutDay[DAY888] = DAY888;
    }

    /** @dev calculate and update variables daily and reset triggers flag */
    function _dailyDifficultyClock() private {
        uint256 currentContractDay = s_currentContractDay;
        uint256 currentBlockDay = ((block.timestamp - i_genesisTs) / 1 days) + 1;

        if (currentBlockDay > currentContractDay) {
            //get last day info ready for calculation
            uint256 newMintCost = s_currentMintCost;
            uint256 newShareRate = s_currentshareRate;
            uint256 newMintableLegacy = s_currentMintableLegacy;
            uint256 newMintPowerBonus = s_currentMintPowerBonus;
            uint256 newEAABonus = s_currentEAABonus;
            uint256 dayDifference = currentBlockDay - currentContractDay;

            /** Reason for a for loop to update Mint supply
             * Ideally, user interaction happens daily, so Mint supply is synced in every day
             *      (cylceDifference = 1)
             * However, if there's no interaction for more than 1 day, then
             *      Mint supply isn't updated correctly due to cylceDifference > 1 day
             * Eg. 2 days of no interaction, then interaction happens in 3rd day.
             *     It's incorrect to only decrease the Mint supply one time as now it's in 3rd day.
             *   And if this happens, there will be no tracked data for the skipped days as not needed
             */
            for (uint256 i; i < dayDifference; i++) {
                newMintCost = (newMintCost * DAILY_MINT_COST_INCREASE_STEP) / PERCENT_BPS;
                newShareRate = (newShareRate * DAILY_SHARE_RATE_INCREASE_STEP) / PERCENT_BPS;
                newMintableLegacy =
                (newMintableLegacy * DAILY_SUPPLY_MINTABLE_REDUCTION) /
                PERCENT_BPS;
                newMintPowerBonus =
                (newMintPowerBonus * DAILY_MINTPOWER_INCREASE_BONUS_REDUCTION) /
                PERCENT_BPS;

                if (newMintCost > 1 ether) {
                    newMintCost = CAPPED_MAX_MINT_COST;
                }

                if (newShareRate > CAPPED_MAX_RATE) newShareRate = CAPPED_MAX_RATE;

                /** @dev leave Legacy production at 1 if MINIMUM_LEGACY_SUPPLY has not been met */
                if (newMintableLegacy < CAPPED_MIN_DAILY_LEGACY_MINTABLE) {
                    if (_getTotalMintedLegacy() >= MINIMUM_LEGACY_SUPPLY) {
                        newMintableLegacy = MINTABLE_LEGACY_END;
                    } else {
                        newMintableLegacy = CAPPED_MIN_DAILY_LEGACY_MINTABLE;
                    }
                }

                if (newMintPowerBonus < CAPPED_MIN_MINTPOWER_BONUS) {
                    newMintPowerBonus = CAPPED_MIN_MINTPOWER_BONUS;
                }

                if (currentBlockDay <= MAX_BONUS_DAY) {
                    newEAABonus -= EAA_BONUSE_FIXED_REDUCTION_PER_DAY;
                } else {
                    newEAABonus = EAA_END;
                }

                emit GlobalDailyDifficultyClockStats(
                    ++currentContractDay,
                    newMintCost,
                    newShareRate,
                    newMintableLegacy,
                    newMintPowerBonus,
                    newEAABonus
                );

            }

            s_currentMintCost = uint64(newMintCost);
            s_currentshareRate = uint72(newShareRate);
            s_currentMintableLegacy = uint96(newMintableLegacy);
            s_currentMintPowerBonus = uint32(newMintPowerBonus);
            s_currentEAABonus = uint32(newEAABonus);
            s_currentContractDay = currentBlockDay;
            s_isGlobalPayoutTriggered = PayoutTriggered.NO;
        }
    }

    /** @dev first created shares will start from the last payout index + 1 (next cycle payout)
     * as first shares will always disqualified from past payouts
     * reduce gas cost needed to loop from first index
     * @param user user address
     * @param isFirstShares flag to only initialize when address is fresh wallet
     */
    function _initFirstSharesCycleIndex(address user, uint256 isFirstShares) internal {
        if (isFirstShares == 1) {
            if (s_cyclePayoutIndex[DAY28] != 0) {
                s_addressCycleToLastClaimIndex[user][DAY28].cycleIndex = uint96(
                    s_cyclePayoutIndex[DAY28] + 1
                );

                s_addressCycleToLastClaimIndex[user][DAY90].cycleIndex = uint96(
                    s_cyclePayoutIndex[DAY90] + 1
                );

                s_addressCycleToLastClaimIndex[user][DAY369].cycleIndex = uint96(
                    s_cyclePayoutIndex[DAY369] + 1
                );

                s_addressCycleToLastClaimIndex[user][DAY888].cycleIndex = uint96(
                    s_cyclePayoutIndex[DAY888] + 1
                );
            }
        }
    }

    /** @dev first created shares will start from the last payout index + 1 (next cycle payout)
     * as first shares will always disqualified from past payouts
     * reduce gas cost needed to loop from first index
     * @param cycleNo cycle day 28, 90, 369, 888
     * @param reward total accumulated reward in cycle day 28, 90, 369, 888
     * @param globalActiveShares global active shares
     * @return index return latest current cycleIndex
     */
    function _calculateCycleRewardPerShare(
        uint256 cycleNo,
        uint256 reward,
        uint256 globalActiveShares
    ) internal returns (uint256 index) {
        s_cyclePayouts[cycleNo] = 0;
        index = ++s_cyclePayoutIndex[cycleNo];
        //add 18 decimals to reward for better precision in calculation
        s_cyclePayoutPerShare[cycleNo][index].payoutPerShare =
        (reward * SCALING_FACTOR_1e18) /
        globalActiveShares;
        s_cyclePayoutPerShare[cycleNo][index].day = getCurrentContractDay();
    }

    /** @dev update with the last index where a user has claimed the payout reward
     * @param user user address
     * @param cycleNo cylce day 28, 90, 369, 888
     * @param userClaimCycleIndex last claimed cycle index
     * @param userClaimSharesIndex last claimed shares index
     */
    function _updateUserClaimIndexes(
        address user,
        uint256 cycleNo,
        uint256 userClaimCycleIndex,
        uint256 userClaimSharesIndex
    ) internal {
        if (userClaimCycleIndex != s_addressCycleToLastClaimIndex[user][cycleNo].cycleIndex)
            s_addressCycleToLastClaimIndex[user][cycleNo].cycleIndex = uint96(userClaimCycleIndex);

        if (userClaimSharesIndex != s_addressCycleToLastClaimIndex[user][cycleNo].sharesIndex)
            s_addressCycleToLastClaimIndex[user][cycleNo].sharesIndex = uint64(
                userClaimSharesIndex
            );
    }

    /** @dev set to YES when any of the cycle days payout is triggered
     * reset to NO in new contract day
     */
    function _setGlobalPayoutTriggered() internal {
        s_isGlobalPayoutTriggered = PayoutTriggered.YES;
    }

    /** @dev add reward into cycle day 28, 90, 369, 888 pool
     * @param cycleNo cycle day 28, 90, 369, 888
     * @param reward reward from distributeETH()
     */
    function _setCyclePayoutPool(uint256 cycleNo, uint256 reward) internal {
        s_cyclePayouts[cycleNo] += reward;
    }

    /** @dev calculate and update the next payout day for specified cycleNo
     * the formula will update the payout day based on current contract day
     * this is to make sure the value is correct when for some reason has skipped more than one cycle payout
     * @param cycleNo cycle day 28, 90, 369, 888
     */
    function _setNextCyclePayoutDay(uint256 cycleNo) internal {
        uint256 maturityDay = s_nextCyclePayoutDay[cycleNo];
        uint256 currentContractDay = s_currentContractDay;
        if (currentContractDay >= maturityDay) {
            s_nextCyclePayoutDay[cycleNo] +=
            cycleNo *
            (((currentContractDay - maturityDay) / cycleNo) + 1);
        }
    }

    /** @dev Calls the getTotalMintedLegacy function in the MintInfo contract through Legacy
     * since GlobalInfo does not directly inherit from MintInfo so
     * _dailyDifficultyClock knows how much Legacy has been already minted
     */
    function _getTotalMintedLegacy() internal view virtual returns (uint256);

    /** Views */
    /** @notice Returns current block timestamp
     * @return currentBlockTs current block timestamp
     */
    function getCurrentBlockTimeStamp() public view returns (uint256) {
        return block.timestamp;
    }

    /** @notice Returns current contract day
     * @return currentContractDay current contract day
     */
    function getCurrentContractDay() public view returns (uint256) {
        return s_currentContractDay;
    }

    /** @notice Returns current mint cost
     * @return currentMintCost current block timestamp
     */
    function getCurrentMintCost() public view returns (uint256) {
        return s_currentMintCost;
    }

    /** @notice Returns current share rate
     * @return currentShareRate current share rate
     */
    function getCurrentShareRate() public view returns (uint256) {
        return s_currentshareRate;
    }

    /** @notice Returns current mintable Legacy
     * @return currentMintableLegacy current mintable Legacy
     */
    function getCurrentMintableLegacy() public view returns (uint256) {
        return s_currentMintableLegacy;
    }

    /** @notice Returns current mint power bonus
     * @return currentMintPowerBonus current mint power bonus
     */
    function getCurrentMintPowerBonus() public view returns (uint256) {
        return s_currentMintPowerBonus;
    }

    /** @notice Returns current contract EAA bonus
     * @return currentEAABonus current EAA bonus
     */
    function getCurrentEAABonus() public view returns (uint256) {
        return s_currentEAABonus;
    }

    /** @notice Returns current cycle index for the specified cycle day
     * @param cycleNo cycle day 28, 90, 369, 888
     * @return currentCycleIndex current cycle index to track the payouts
     */
    function getCurrentCycleIndex(uint256 cycleNo) public view returns (uint256) {
        return s_cyclePayoutIndex[cycleNo];
    }

    /** @notice Returns whether payout is triggered successfully in any cylce day
     * @return isTriggered 0 or 1, 0= No, 1=Yes
     */
    function getGlobalPayoutTriggered() public view returns (PayoutTriggered) {
        return s_isGlobalPayoutTriggered;
    }

    /** @notice Returns the distributed pool reward for the specified cycle day
     * @param cycleNo cycle day 28, 90, 369, 888
     * @return currentPayoutPool current accumulated payout pool
     */
    function getCyclePayoutPool(uint256 cycleNo) public view returns (uint256) {
        return s_cyclePayouts[cycleNo];
    }

    /** @notice Returns the calculated payout per share and contract day for the specified cycle day and index
     * @param cycleNo cycle day 28, 90, 369, 888
     * @param index cycle index
     * @return payoutPerShare calculated payout per share
     * @return triggeredDay the day when payout was triggered to perform calculation
     */
    function getPayoutPerShare(
        uint256 cycleNo,
        uint256 index
    ) public view returns (uint256, uint256) {
        return (
        s_cyclePayoutPerShare[cycleNo][index].payoutPerShare,
        s_cyclePayoutPerShare[cycleNo][index].day
        );
    }

    /** @notice Returns user's last claimed shares payout indexes for the specified cycle day
     * @param user user address
     * @param cycleNo cycle day 28, 90, 369, 888
     * @return cycleIndex cycle index
     * @return sharesIndex shares index

     */
    function getUserLastClaimIndex(
        address user,
        uint256 cycleNo
    ) public view returns (uint256 cycleIndex, uint256 sharesIndex) {
        return (
        s_addressCycleToLastClaimIndex[user][cycleNo].cycleIndex,
        s_addressCycleToLastClaimIndex[user][cycleNo].sharesIndex
        );
    }

    /** @notice Returns contract deployment block timestamp
     * @return genesisTs deployed timestamp
     */
    function genesisTs() public view returns (uint256) {
        return i_genesisTs;
    }

    /** @notice Returns next payout day for the specified cycle day
     * @param cycleNo cycle day 28, 90, 369, 888
     * @return nextPayoutDay next payout day
     */
    function getNextCyclePayoutDay(uint256 cycleNo) public view returns (uint256) {
        return s_nextCyclePayoutDay[cycleNo];
    }
}

File 9 of 16 : MintInfo.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "../libs/calcFunctions.sol";

//custom errors
    error Legacy_InvalidMintLength();
    error Legacy_InvalidMintPower();
    error Legacy_NoMintExists();
    error Legacy_MintHasClaimed();
    error Legacy_MintNotMature();

abstract contract MintInfo {
    //variables
    /** @dev track global legacyRank */
    uint256 private s_globalLRank;
    /** @dev track total mint claimed */
    uint256 private s_globalMintClaim;
    /** @dev track total Legacy rewards minted */
    uint256 private s_totalMintedLegacy;
    /** @dev track total Legacy minting */
    uint256 private s_globalLegacyMinting;
    /** @dev track total Legacy penalty */
    uint256 private s_globalLegacyMintPenalty;
    /** @dev track global mint power */
    uint256 private s_globalMintPower;

    //mappings
    /** @dev track address => mintId */
    mapping(address => uint256) private s_addressMId;
    /** @dev track address, mintId => lRank info (gLrank, gMintPower) */
    mapping(address => mapping(uint256 => LRankInfo)) private s_addressMIdToLRankInfo;
    /** @dev track global lRank => mintInfo*/
    mapping(uint256 => UserMintInfo) private s_lRankToMintInfo;

    //structs
    struct UserMintInfo {
        uint8 mintPower;
        uint16 numOfDays;
        uint96 mintableLegacy;
        uint48 mintStartTs;
        uint48 maturityTs;
        uint32 mintPowerBonus;
        uint32 EAABonus;
        uint128 mintedLegacy;
        uint64 mintCost;
        MintStatus status;
    }

    struct LRankInfo {
        uint256 lRank;
        uint256 gMintPower;
    }

    struct UserMint {
        uint256 mId;
        uint256 lRank;
        uint256 gMintPower;
        UserMintInfo mintInfo;
    }

    //events
    event MintStarted(
        address indexed user,
        uint256 indexed lRank,
        uint256 indexed gMintpower,
        UserMintInfo userMintInfo
    );

    event MintClaimed(
        address indexed user,
        uint256 indexed lRank,
        uint256 rewardMinted,
        uint256 indexed penalty,
        uint256 mintPenalty
    );

    //functions
    /** @dev create a new builder
     * @param user user address
     * @param mintPower mint power
     * @param numOfDays mint lenght
     * @param mintableLegacy mintable Legacy
     * @param mintPowerBonus mint power bonus
     * @param EAABonus EAA bonus
     * @param gMintPower global mint power
     * @param currentLRank current global lRank
     * @param mintCost actual mint cost paid for a mint
     */
    function _startMint(
        address user,
        uint256 mintPower,
        uint256 numOfDays,
        uint256 mintableLegacy,
        uint256 mintPowerBonus,
        uint256 EAABonus,
        uint256 gMintPower,
        uint256 currentLRank,
        uint256 mintCost
    ) internal returns (uint256 mintable) {
        if (numOfDays == 0 || numOfDays > MAX_MINT_LENGTH) revert Legacy_InvalidMintLength();
        if (mintPower == 0 || mintPower > MAX_MINT_POWER_CAP) revert Legacy_InvalidMintPower();

        //calculate builder reward up front with the provided params
        mintable = calculateMintReward(mintPower, numOfDays, mintableLegacy, EAABonus);

        //store variables into mint info
        UserMintInfo memory userMintInfo = UserMintInfo({
            mintPower: uint8(mintPower),
            numOfDays: uint16(numOfDays),
            mintableLegacy: uint96(mintable),
            mintPowerBonus: uint32(mintPowerBonus),
            EAABonus: uint32(EAABonus),
            mintStartTs: uint48(block.timestamp),
            maturityTs: uint48(block.timestamp + (numOfDays * SECONDS_IN_DAY)),
            mintedLegacy: 0,
            mintCost: uint64(mintCost),
            status: MintStatus.ACTIVE
        });

        /** s_addressMId[user] tracks mintId for each addrress
         * s_addressMIdToLRankInfo[user][id] tracks current mint lRank and gPowerMint
         *  s_lRankToMintInfo[currentLRank] stores mint info
         */
        uint256 id = ++s_addressMId[user];
        s_addressMIdToLRankInfo[user][id].lRank = currentLRank;
        s_addressMIdToLRankInfo[user][id].gMintPower = gMintPower;
        s_lRankToMintInfo[currentLRank] = userMintInfo;

        emit MintStarted(user, currentLRank, gMintPower, userMintInfo);
    }

    /** @dev create new mint in a batch of up to max 100 mints with the same mint length
     * @param user user address
     * @param mintPower mint power
     * @param numOfDays mint lenght
     * @param mintableLegacy mintable Legacy
     * @param mintPowerBonus mint power bonus
     * @param EAABonus EAA bonus
     * @param count count of mints
     * @param mintCost actual mint cost paid for a mint
     */
    function _startBatchMint(
        address user,
        uint256 mintPower,
        uint256 numOfDays,
        uint256 mintableLegacy,
        uint256 mintPowerBonus,
        uint256 EAABonus,
        uint256 count,
        uint256 mintCost
    ) internal {
        uint256 gMintPower = s_globalMintPower;
        uint256 currentLRank = s_globalLRank;
        uint256 gMinting = s_globalLegacyMinting;

        for (uint256 i = 0; i < count; i++) {
            gMintPower += mintPower;
            gMinting += _startMint(
                user,
                mintPower,
                numOfDays,
                mintableLegacy,
                mintPowerBonus,
                EAABonus,
                gMintPower,
                ++currentLRank,
                mintCost
            );
        }
        _updateMintStats(currentLRank, gMintPower, gMinting);
    }

    /** @dev update variables
     * @param currentLRank current lRank
     * @param gMintPower current global mint power
     * @param gMinting current global minting
     */
    function _updateMintStats(uint256 currentLRank, uint256 gMintPower, uint256 gMinting) internal {
        s_globalLRank = currentLRank;
        s_globalMintPower = gMintPower;
        s_globalLegacyMinting = gMinting;
    }

    /** @dev calculate reward for claim mint.
     * Claim mint has maturity check
     * @param user user address
     * @param id mint id
     * @param action claim mint
     * @return reward calculated final reward after all bonuses and penalty (if any)
     */
    function _claimMint(
        address user,
        uint256 id,
        MintAction action
    ) internal returns (uint256 reward) {
        uint256 lRank = s_addressMIdToLRankInfo[user][id].lRank;
        uint256 gMintPower = s_addressMIdToLRankInfo[user][id].gMintPower;
        if (lRank == 0) revert Legacy_NoMintExists();

        UserMintInfo memory mint = s_lRankToMintInfo[lRank];
        if (mint.status == MintStatus.CLAIMED) revert Legacy_MintHasClaimed();

        //Only check maturity for claim mint action
        if (mint.maturityTs > block.timestamp && action == MintAction.CLAIM)
            revert Legacy_MintNotMature();

        s_globalLegacyMinting -= mint.mintableLegacy;
        s_totalMintedLegacy += mint.mintableLegacy;
        reward = _calculateClaimReward(user, lRank, gMintPower, mint, action);
    }

    /** @dev calculate reward up to 100 claims for batch claim function. Only calculate active and matured mints.
     * @param user user address
     * @return reward total batch claims final calculated reward after all bonuses and penalty (if any)
     */
    function _batchClaimMint(address user) internal returns (uint256 reward) {
        uint256 maxId = s_addressMId[user];
        uint256 claimCount;
        uint256 lRank;
        uint256 gMinting;
        UserMintInfo memory mint;

        for (uint256 i = 1; i <= maxId; i++) {
            lRank = s_addressMIdToLRankInfo[user][i].lRank;
            mint = s_lRankToMintInfo[lRank];
            if (mint.status == MintStatus.ACTIVE && block.timestamp >= mint.maturityTs) {
                reward += _calculateClaimReward(
                    user,
                    lRank,
                    s_addressMIdToLRankInfo[user][i].gMintPower,
                    mint,
                    MintAction.CLAIM
                );

                gMinting += mint.mintableLegacy;
                ++claimCount;
            }

            if (claimCount == 100) break;
        }

        s_globalLegacyMinting -= gMinting;
        s_totalMintedLegacy += gMinting;
    }

    /** @dev calculate final reward with bonuses and penalty (if any)
     * @param user user address
     * @param lRank builder's lRank
     * @param gMintPower mint's gMintPower
     * @param userMintInfo mint's info
     * @param action claim mint
     * @return reward calculated final reward after all bonuses and penalty (if any)
     */
    function _calculateClaimReward(
        address user,
        uint256 lRank,
        uint256 gMintPower,
        UserMintInfo memory userMintInfo,
        MintAction action
    ) private returns (uint256 reward) {
        if (action == MintAction.CLAIM) s_lRankToMintInfo[lRank].status = MintStatus.CLAIMED;

        uint256 penaltyAmount;
        uint256 penalty;
        uint256 bonus;

        //only calculate penalty when current block timestamp > maturity timestamp
        if (block.timestamp > userMintInfo.maturityTs) {
            penalty = calculateClaimMintPenalty(block.timestamp - userMintInfo.maturityTs);
        }

        //Only Claim action has mintPower bonus
        if (action == MintAction.CLAIM) {
            bonus = calculateMintPowerBonus(
                userMintInfo.mintPowerBonus,
                userMintInfo.mintPower,
                gMintPower,
                s_globalMintPower
            );
        }

        //mintPowerBonus has scaling factor of 1e6, so divide by 1e6
        reward = uint256(userMintInfo.mintableLegacy) + (bonus / SCALING_FACTOR_1e6);
        penaltyAmount = (reward * penalty) / 100;
        reward -= penaltyAmount;

        if (action == MintAction.CLAIM) ++s_globalMintClaim;
        if (penaltyAmount != 0) s_globalLegacyMintPenalty += penaltyAmount;

        //only stored minted amount for claim mint
        if (action == MintAction.CLAIM) s_lRankToMintInfo[lRank].mintedLegacy = uint128(reward);

        emit MintClaimed(user, lRank, reward, penalty, penaltyAmount);
    }

    //views
    /** @notice Returns the latest Mint Id of an address
     * @param user address
     * @return mId latest mint id
     */
    function getUserLatestMintId(address user) public view returns (uint256) {
        return s_addressMId[user];
    }

    /** @notice Returns mint info of an address + mint id
     * @param user address
     * @param id mint id
     * @return mintInfo user mint info
     */
    function getUserMintInfo(
        address user,
        uint256 id
    ) public view returns (UserMintInfo memory mintInfo) {
        return s_lRankToMintInfo[s_addressMIdToLRankInfo[user][id].lRank];
    }

    /** @notice Return all mints info of an address
     * @param user address
     * @return mintInfos all mints info of an address including mint id, lRank and gMintPower
     */
    function getUserMints(address user) public view returns (UserMint[] memory mintInfos) {
        uint256 count = s_addressMId[user];
        mintInfos = new UserMint[](count);

        for (uint256 i = 1; i <= count; i++) {
            mintInfos[i - 1] = UserMint({
                mId: i,
                lRank: s_addressMIdToLRankInfo[user][i].lRank,
                gMintPower: s_addressMIdToLRankInfo[user][i].gMintPower,
                mintInfo: getUserMintInfo(user, i)
            });
        }
    }

    /** @notice Return current global legacyRank
     * @return globalLRank global lRank
     */
    function getGlobalLRank() public view returns (uint256) {
        return s_globalLRank;
    }

    /** @notice Return current gobal mint power
     * @return globalMintPower global mint power
     */
    function getGlobalMintPower() public view returns (uint256) {
        return s_globalMintPower;
    }

    /** @notice Return total mints claimed
     * @return totalMintClaimed total mints claimed
     */
    function getTotalMintClaim() public view returns (uint256) {
        return s_globalMintClaim;
    }

    /** @notice Return total minted Legacy
     * @return s_totalMintedLegacy total minted Legacy
     */
    function getTotalMintedLegacy() public view returns (uint256) {
        return s_totalMintedLegacy;
    }

    /** @notice Return total active mints (exluded claimed mints)
     * @return totalActiveMints total active mints
     */
    function getTotalActiveMints() public view returns (uint256) {
        return s_globalLRank - s_globalMintClaim;
    }

    /** @notice Return total minting Legacy
     * @return totalMinting total minting Legacy
     */
    function getTotalMinting() public view returns (uint256) {
        return s_globalLegacyMinting;
    }

    /** @notice Return total Legacy penalty
     * @return totalLegacyPenalty total Legacy penalty
     */
    function getTotalMintPenalty() public view returns (uint256) {
        return s_globalLegacyMintPenalty;
    }
}

File 10 of 16 : OwnerInfo.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "@openzeppelin/contracts/utils/Context.sol";

    error Legacy_NotOnwer();

abstract contract OwnerInfo is Context {
    address private s_owner;

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        s_owner = _msgSender();
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (s_owner != _msgSender()) revert Legacy_NotOnwer();
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public onlyOwner {
        _setOwner(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public onlyOwner {
        _setOwner(newOwner);
    }

    function _setOwner(address newOwner) private {
        s_owner = newOwner;
    }
}

File 11 of 16 : StakeInfo.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "../libs/calcFunctions.sol";

//custom errors
    error Legacy_InvalidStakeLength();
    error Legacy_RequireOneMinimumShare();
    error Legacy_ExceedMaxAmountPerStake();
    error Legacy_NoStakeExists();
    error Legacy_StakeHasEnded();
    error Legacy_StakeNotMatured();
    error Legacy_MaxedWalletStakes();

abstract contract StakeInfo {
    //Variables
    /** @dev track global stake Id */
    uint256 private s_globalStakeId;
    /** @dev track global shares */
    uint256 private s_globalShares;
    /** @dev track global expired shares */
    uint256 private s_globalExpiredShares;
    /** @dev track global staked Legacy */
    uint256 private s_globalLegacyStaked;
    /** @dev track global end stake penalty */
    uint256 private s_globalStakePenalty;
    /** @dev track global ended stake */
    uint256 private s_globalStakeEnd;

    //mappings
    /** @dev track address => stakeId */
    mapping(address => uint256) private s_addressSId;
    /** @dev track address, stakeId => global stake Id */
    mapping(address => mapping(uint256 => uint256)) private s_addressSIdToGlobalStakeId;
    /** @dev track global stake Id => stake info */
    mapping(uint256 => UserStakeInfo) private s_globalStakeIdToStakeInfo;

    /** @dev track address => shares Index */
    mapping(address => uint256) private s_userSharesIndex;
    /** @dev track user total active shares by user shares index
     * s_addressIdToActiveShares[user][index] = UserActiveShares (contract day, total user active shares)
     * works like a snapshot or log when user shares has changed (increase/decrease)
     */
    mapping(address => mapping(uint256 => UserActiveShares)) private s_addressIdToActiveShares;

    //structs
    struct UserStakeInfo {
        uint152 legacyAmount;
        uint128 shares;
        uint16 numOfDays;
        uint48 stakeStartTs;
        uint48 maturityTs;
        StakeStatus status;
    }

    struct UserStake {
        uint256 sId;
        uint256 globalStakeId;
        UserStakeInfo stakeInfo;
    }

    struct UserActiveShares {
        uint256 day;
        uint256 activeShares;
    }

    //events
    event StakeStarted(
        address indexed user,
        uint256 indexed globalStakeId,
        uint256 numOfDays,
        UserStakeInfo indexed userStakeInfo
    );

    event StakeEnded(
        address indexed user,
        uint256 indexed globalStakeId,
        uint256 legacyAmount,
        uint256 indexed penalty,
        uint256 penaltyAmount
    );

    //functions
    /** @dev create a new stake
     * @param user user address
     * @param amount Legacy amount
     * @param numOfDays stake length
     * @param shareRate current share rate
     * @param day current contract day
     * @param isPayoutTriggered has global payout triggered
     * @return isFirstShares first created shares or not
     */
    function _startStake(
        address user,
        uint256 amount,
        uint256 numOfDays,
        uint256 shareRate,
        uint256 day,
        PayoutTriggered isPayoutTriggered
    ) internal returns (uint256 isFirstShares) {
        uint256 sId = ++s_addressSId[user];
        if (sId > MAX_STAKE_PER_WALLET) revert Legacy_MaxedWalletStakes();
        if (numOfDays < MIN_STAKE_LENGTH || numOfDays > MAX_STAKE_LENGTH)
            revert Legacy_InvalidStakeLength();

        //calculate shares
        uint256 shares = calculateShares(amount, numOfDays, shareRate);

        if (shares / SCALING_FACTOR_1e18 < 1) revert Legacy_RequireOneMinimumShare();

        uint256 currentGStakeId = ++s_globalStakeId;
        uint256 maturityTs;

        maturityTs = block.timestamp + (numOfDays * SECONDS_IN_DAY);

        UserStakeInfo memory userStakeInfo = UserStakeInfo({
            legacyAmount: uint152(amount),
            shares: uint128(shares),
            numOfDays: uint16(numOfDays),
            stakeStartTs: uint48(block.timestamp),
            maturityTs: uint48(maturityTs),
            status: StakeStatus.ACTIVE
        });

        /** s_addressSId[user] tracks stake Id for each address
         * s_addressSIdToGlobalStakeId[user][id] tracks stack id to global stake Id
         * s_globalStakeIdToStakeInfo[currentGStakeId] stores stake info
         */
        s_addressSIdToGlobalStakeId[user][sId] = currentGStakeId;
        s_globalStakeIdToStakeInfo[currentGStakeId] = userStakeInfo;

        //update shares changes
        isFirstShares = _updateSharesStats(
            user,
            shares,
            amount,
            day,
            isPayoutTriggered,
            StakeAction.START
        );

        emit StakeStarted(user, currentGStakeId, numOfDays, userStakeInfo);
    }

    /** @dev end stake and calculate principle with penalties (if any)
     * @param user user address
     * @param id stake Id
     * @param day current contract day
     * @param action end stake
     * @param isPayoutTriggered has global payout triggered
     * @return legacy principle
     */
    function _endStake(
        address user,
        uint256 id,
        uint256 day,
        StakeAction action,
        PayoutTriggered isPayoutTriggered
    ) internal returns (uint256 legacy) {
        uint256 globalStakeId = s_addressSIdToGlobalStakeId[user][id];
        if (globalStakeId == 0) revert Legacy_NoStakeExists();

        UserStakeInfo memory userStakeInfo = s_globalStakeIdToStakeInfo[globalStakeId];
        if (userStakeInfo.status == StakeStatus.ENDED) revert Legacy_StakeHasEnded();

        //update shares changes
        uint256 shares = userStakeInfo.shares;
        _updateSharesStats(user, shares, userStakeInfo.legacyAmount, day, isPayoutTriggered, action);

        if (action == StakeAction.END) {
            ++s_globalStakeEnd;
            s_globalStakeIdToStakeInfo[globalStakeId].status = StakeStatus.ENDED;
        }

        legacy = _calculatePrinciple(user, globalStakeId, userStakeInfo);
    }

    /** @dev update shares changes to track when user shares has changed, this affect the payout calculation
     * @param user user address
     * @param shares shares
     * @param amount Legacy amount
     * @param day current contract day
     * @param isPayoutTriggered has global payout triggered
     * @param action start stake or end stake
     * @return isFirstShares first created shares or not
     */
    function _updateSharesStats(
        address user,
        uint256 shares,
        uint256 amount,
        uint256 day,
        PayoutTriggered isPayoutTriggered,
        StakeAction action
    ) private returns (uint256 isFirstShares) {
        //Get previous active shares to calculate new shares change
        uint256 index = s_userSharesIndex[user];
        uint256 previousShares = s_addressIdToActiveShares[user][index].activeShares;

        if (action == StakeAction.START) {
            //return 1 if this is a new wallet address
            //this is used to initialize last claim index to the latest cycle index
            if (index == 0) isFirstShares = 1;

            s_addressIdToActiveShares[user][++index].activeShares = previousShares + shares;
            s_globalShares += shares;
            s_globalLegacyStaked += amount;
        } else {
            s_addressIdToActiveShares[user][++index].activeShares = previousShares - shares;
            s_globalExpiredShares += shares;
            s_globalLegacyStaked -= amount;
        }

        //If global payout hasn't triggered, use current contract day to eligible for payout
        //If global payout has triggered, then start with next contract day as it's no longer eligible to claim latest payout
        s_addressIdToActiveShares[user][index].day = uint128(
            isPayoutTriggered == PayoutTriggered.NO ? day : day + 1
        );

        s_userSharesIndex[user] = index;
    }

    /** @dev calculate stake principle and apply penalty (if any)
     * @param user user address
     * @param globalStakeId global stake Id
     * @param userStakeInfo stake info
     * @return principle calculated principle after penalty (if any)
     */
    function _calculatePrinciple(
        address user,
        uint256 globalStakeId,
        UserStakeInfo memory userStakeInfo
    ) internal returns (uint256 principle) {
        uint256 legacyAmount = userStakeInfo.legacyAmount;
        //penalty is in percentage
        uint256 penalty = calculateEndStakePenalty(
            userStakeInfo.stakeStartTs,
            userStakeInfo.maturityTs,
            block.timestamp
        );

        uint256 penaltyAmount;
        penaltyAmount = (legacyAmount * penalty) / 100;
        principle = legacyAmount - penaltyAmount;
        s_globalStakePenalty += penaltyAmount;

        emit StakeEnded(user, globalStakeId, principle, penalty, penaltyAmount);
    }

    //Views
    /** @notice get global shares
     * @return globalShares global shares
     */
    function getGlobalShares() public view returns (uint256) {
        return s_globalShares;
    }

    /** @notice get global expired shares
     * @return globalExpiredShares global expired shares
     */
    function getGlobalExpiredShares() public view returns (uint256) {
        return s_globalExpiredShares;
    }

    /** @notice get global active shares
     * @return globalActiveShares global active shares
     */
    function getGlobalActiveShares() public view returns (uint256) {
        return s_globalShares - s_globalExpiredShares;
    }

    /** @notice get total Legacy staked
     * @return globalLegacyStaked total Legacy staked
     */
    function getTotalLegacyStaked() public view returns (uint256) {
        return s_globalLegacyStaked;
    }

    /** @notice get global stake id
     * @return globalStakeId global stake id
     */
    function getGlobalStakeId() public view returns (uint256) {
        return s_globalStakeId;
    }

    /** @notice get global active stakes
     * @return globalActiveStakes global active stakes
     */
    function getGlobalActiveStakes() public view returns (uint256) {
        return s_globalStakeId - getTotalStakeEnd();
    }

    /** @notice get total stake ended
     * @return totalStakeEnded total stake ended
     */
    function getTotalStakeEnd() public view returns (uint256) {
        return s_globalStakeEnd;
    }

    /** @notice get total end stake penalty
     * @return totalEndStakePenalty total end stake penalty
     */
    function getTotalStakePenalty() public view returns (uint256) {
        return s_globalStakePenalty;
    }

    /** @notice get user latest shares index
     * @return latestSharesIndex latest shares index
     */
    function getUserLatestShareIndex(address user) public view returns (uint256) {
        return s_userSharesIndex[user];
    }

    /** @notice get user current active shares
     * @return currentActiveShares current active shares
     */
    function getUserCurrentActiveShares(address user) public view returns (uint256) {
        return s_addressIdToActiveShares[user][getUserLatestShareIndex(user)].activeShares;
    }

    /** @notice get user active shares at sharesIndex
     * @return activeShares active shares at sharesIndex
     */
    function getUserActiveShares(
        address user,
        uint256 sharesIndex
    ) internal view returns (uint256) {
        return s_addressIdToActiveShares[user][sharesIndex].activeShares;
    }

    /** @notice get user active shares contract day at sharesIndex
     * @return activeSharesDay active shares contract day at sharesIndex
     */
    function getUserActiveSharesDay(
        address user,
        uint256 sharesIndex
    ) internal view returns (uint256) {
        return s_addressIdToActiveShares[user][sharesIndex].day;
    }

    /** @notice get stake info with stake id
     * @return stakeInfo stake info
     */
    function getUserStakeInfo(address user, uint256 id) public view returns (UserStakeInfo memory) {
        return s_globalStakeIdToStakeInfo[s_addressSIdToGlobalStakeId[user][id]];
    }

    /** @notice get all stake info of an address
     * @return stakeInfos all stake info of an address
     */
    function getUserStakes(address user) public view returns (UserStake[] memory) {
        uint256 count = s_addressSId[user];
        UserStake[] memory stakes = new UserStake[](count);

        for (uint256 i = 1; i <= count; i++) {
            stakes[i - 1] = UserStake({
                sId: i,
                globalStakeId: uint128(s_addressSIdToGlobalStakeId[user][i]),
                stakeInfo: getUserStakeInfo(user, i)
            });
        }

        return stakes;
    }
}

File 12 of 16 : ILEGACY.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

interface ILEGACY {
    function balanceOf(address account) external returns (uint256);

    function getBalance() external;

    function mintLPTokens() external;

    function burnLPTokens() external;
}

File 13 of 16 : ILegacyOnBurn.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

interface ILegacyOnBurn {
    function onBurn(address user, uint256 amount) external;
}

File 14 of 16 : calcFunctions.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "./constant.sol";
import "./enum.sol";

//Legacy

/** @notice get batch mint cost
 * @param mintPower mint power (1 - 100)
 * @param count number of mints
 * @return mintCost total mint cost
 */
    function getBatchMintCost(
        uint256 mintPower,
        uint256 count,
        uint256 mintCost
    ) pure returns (uint256) {
        return (mintCost * mintPower * count) / MAX_MINT_POWER_CAP;
    }

//MintInfo

/** @notice the formula to calculate mint reward at create new mint
 * @param mintPower mint power 1 - 100
 * @param numOfDays mint length 1 - 280
 * @param mintableLegacy current contract day mintable legacy
 * @param EAABonus current contract day EAA Bonus
 * @return reward base legacy amount
 */
    function calculateMintReward(
        uint256 mintPower,
        uint256 numOfDays,
        uint256 mintableLegacy,
        uint256 EAABonus
    ) pure returns (uint256 reward) {
        uint256 baseReward = (mintableLegacy * mintPower * numOfDays);
        if (numOfDays != 1)
            baseReward -= (baseReward * MINT_DAILY_REDUCTION * (numOfDays - 1)) / PERCENT_BPS;

        reward = baseReward;
        if (EAABonus != 0) {
            //EAA Bonus has 1e6 scaling, so here divide by 1e6
            reward += ((baseReward * EAABonus) / 100 / SCALING_FACTOR_1e6);
        }

        reward /= MAX_MINT_POWER_CAP;
    }

/** @notice the formula to calculate bonus reward
 * heavily influenced by the difference between current global mint power and user mint's global mint power
 * @param mintPowerBonus mint power bonus from mintinfo
 * @param mintPower mint power 1 - 100 from mintinfo
 * @param gMintPower global mint power from mintinfo
 * @param globalMintPower current global mint power
 * @return bonus bonus amount in legacy
 */
    function calculateMintPowerBonus(
        uint256 mintPowerBonus,
        uint256 mintPower,
        uint256 gMintPower,
        uint256 globalMintPower
    ) pure returns (uint256 bonus) {
        if (globalMintPower <= gMintPower) return 0;
        bonus = (((mintPowerBonus * mintPower * (globalMintPower - gMintPower)) * SCALING_FACTOR_1e18) /
        MAX_MINT_POWER_CAP);
    }

/**
 * @dev Return penalty percentage based on number of days late after the grace period of 7 days
 * @param secsLate seconds late (block timestamp - maturity timestamp)
 * @return penalty penalty in percentage
 */
    function calculateClaimMintPenalty(uint256 secsLate) pure returns (uint256 penalty) {
        if (secsLate <= CLAIM_MINT_GRACE_PERIOD * SECONDS_IN_DAY) return 0;
        if (secsLate <= (CLAIM_MINT_GRACE_PERIOD + 1) * SECONDS_IN_DAY) return 1;
        if (secsLate <= (CLAIM_MINT_GRACE_PERIOD + 2) * SECONDS_IN_DAY) return 3;
        if (secsLate <= (CLAIM_MINT_GRACE_PERIOD + 3) * SECONDS_IN_DAY) return 8;
        if (secsLate <= (CLAIM_MINT_GRACE_PERIOD + 4) * SECONDS_IN_DAY) return 17;
        if (secsLate <= (CLAIM_MINT_GRACE_PERIOD + 5) * SECONDS_IN_DAY) return 35;
        if (secsLate <= (CLAIM_MINT_GRACE_PERIOD + 6) * SECONDS_IN_DAY) return 72;
        return 99;
    }

//StakeInfo

    error Legacy_AtLeastHalfMaturity();

/** @notice get max stake length
 * @return maxStakeLength max stake length
 */
    function getMaxStakeLength() pure returns (uint256) {
        return MAX_STAKE_LENGTH;
    }

/** @notice calculate shares and shares bonus
 * @param amount legacy amount
 * @param noOfDays stake length
 * @param shareRate current contract share rate
 * @return shares calculated shares in 18 decimals
 */
    function calculateShares(
        uint256 amount,
        uint256 noOfDays,
        uint256 shareRate
    ) pure returns (uint256) {
        uint256 shares = amount;
        shares += (shares * calculateShareBonus(amount, noOfDays)) / SCALING_FACTOR_1e11;
        shares /= (shareRate / SCALING_FACTOR_1e18);
        return shares;
    }

/** @notice calculate share bonus
 * @param amount legacy amount
 * @param noOfDays stake length
 * @return shareBonus calculated shares bonus in 11 decimals
 */
    function calculateShareBonus(uint256 amount, uint256 noOfDays) pure returns (uint256 shareBonus) {
        uint256 cappedExtraDays = noOfDays <= LPB_MAX_DAYS ? noOfDays : LPB_MAX_DAYS;
        uint256 cappedStakedLegacy = amount <= BPB_MAX_LEGACY ? amount : BPB_MAX_LEGACY;
        shareBonus =
        ((cappedExtraDays * SCALING_FACTOR_1e11) / LPB_PER_PERCENT) +
        ((cappedStakedLegacy * SCALING_FACTOR_1e11) / BPB_PER_PERCENT);
        return shareBonus;
    }

/** @notice calculate end stake penalty
 * @param stakeStartTs start stake timestamp
 * @param maturityTs  maturity timestamp
 * @param currentBlockTs current block timestamp
 * @return penalty penalty in percentage
 */
    function calculateEndStakePenalty(
        uint256 stakeStartTs,
        uint256 maturityTs,
        uint256 currentBlockTs
    ) view returns (uint256) {
        //Matured, then calculate and return penalty
        if (currentBlockTs > maturityTs) {
            uint256 lateSec = currentBlockTs - maturityTs;
            uint256 gracePeriodSec = END_STAKE_GRACE_PERIOD * SECONDS_IN_DAY;
            if (lateSec <= gracePeriodSec) return 0;
            return max((min((lateSec - gracePeriodSec), 1) / SECONDS_IN_DAY) + 1, 99);
        }

        //Emergency End Stake
        //Not allow to EES below 50% maturity
        if (block.timestamp < stakeStartTs + (maturityTs - stakeStartTs) / 2)
            revert Legacy_AtLeastHalfMaturity();

        //50% penalty for EES before maturity timestamp
        return 50;
    }

//a - input to check against b
//b - minimum number
    function min(uint256 a, uint256 b) pure returns (uint256) {
        if (a > b) return a;
        return b;
    }

//a - input to check against b
//b - maximum number
    function max(uint256 a, uint256 b) pure returns (uint256) {
        if (a > b) return b;
        return a;
    }

File 15 of 16 : constant.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

// ===================== common ==========================================
uint256 constant SECONDS_IN_DAY = 86400;
uint256 constant SCALING_FACTOR_1e3 = 1e3;
uint256 constant SCALING_FACTOR_1e6 = 1e6;
uint256 constant SCALING_FACTOR_1e7 = 1e7;
uint256 constant SCALING_FACTOR_1e11 = 1e11;
uint256 constant SCALING_FACTOR_1e18 = 1e18;

// ===================== Legacy ==========================================
uint256 constant PERCENT_TO_BUY_AND_BURN = 21_00;
uint256 constant TITANX_PERCENT_TO_BUY_AND_BURN = 10_00;
uint256 constant PERCENT_TO_CYCLE_PAYDAYS = 65_00;
uint256 constant PERCENT_TO_GENESIS = 4_00;

uint256 constant INCENTIVE_FEE_PERCENT = 6600;
uint256 constant INCENTIVE_FEE_PERCENT_BASE = 1_000_000;
uint256 constant INITAL_LP_TOKENS = 3_750_000_000 ether;
// ===================== globalInfo ==========================================
//Legacy Supply Variables
uint256 constant DAILY_SUPPLY_MINTABLE_REDUCTION = 99_65;
uint256 constant START_MAX_MINTABLE_PER_DAY = 100 ether;
uint256 constant CAPPED_MIN_DAILY_LEGACY_MINTABLE = 1 ether;
uint256 constant MINTABLE_LEGACY_END = 0;
uint256 constant MINIMUM_LEGACY_SUPPLY = 21_000_000 ether;

//EAA Variables
uint256 constant EAA_START = 10 * SCALING_FACTOR_1e6;
uint256 constant EAA_BONUSE_FIXED_REDUCTION_PER_DAY = 28_571;
uint256 constant EAA_END = 0;
uint256 constant MAX_BONUS_DAY = 350;

//Builder Cost Variables
uint256 constant START_MAX_MINT_COST = 0.2 ether;
uint256 constant CAPPED_MAX_MINT_COST = 1 ether;
uint256 constant DAILY_MINT_COST_INCREASE_STEP = 100_08;

//BuilderPower Bonus Variables
uint256 constant START_MINTPOWER_INCREASE_BONUS = 3 * SCALING_FACTOR_1e6; //starts at 3 with 1e6 scaling factor
uint256 constant CAPPED_MIN_MINTPOWER_BONUS = 3 * SCALING_FACTOR_1e3; //capped min of 0.003 * 1e6
uint256 constant DAILY_MINTPOWER_INCREASE_BONUS_REDUCTION = 99_65;

//Share Rate Variables
uint256 constant START_SHARE_RATE = 800 ether;
uint256 constant DAILY_SHARE_RATE_INCREASE_STEP = 100_03;
uint256 constant CAPPED_MAX_RATE = 2_800 ether;

//Cycle Variables
uint256 constant DAY28 = 28;
uint256 constant DAY90 = 90;
uint256 constant DAY369 = 369;
uint256 constant DAY888 = 888;
uint256 constant CYCLE_28_PERCENT = 24_00;
uint256 constant CYCLE_90_PERCENT = 34_00;
uint256 constant CYCLE_369_PERCENT = 30_00;
uint256 constant CYCLE_888_PERCENT = 12_00;
uint256 constant PERCENT_BPS = 100_00;

// ===================== mintInfo ==========================================
uint256 constant MAX_MINT_POWER_CAP = 100;
uint256 constant MAX_MINT_LENGTH = 280;
uint256 constant CLAIM_MINT_GRACE_PERIOD = 7;
uint256 constant MAX_BATCH_MINT_COUNT = 100;
uint256 constant MAX_MINT_PER_WALLET = 1000;
uint256 constant MINT_DAILY_REDUCTION = 11;

// ===================== stakeInfo ==========================================
uint256 constant MAX_STAKE_PER_WALLET = 1000;
uint256 constant MIN_STAKE_LENGTH = 28;
uint256 constant MAX_STAKE_LENGTH = 3500;
uint256 constant END_STAKE_GRACE_PERIOD = 7;

/* Stake Longer Pays Better bonus */
uint256 constant LPB_MAX_DAYS = 2888;
uint256 constant LPB_PER_PERCENT = 825;

/* Stake Bigger Pays Better bonus */
uint256 constant BPB_MAX_LEGACY = 640_000 * SCALING_FACTOR_1e18;
uint256 constant BPB_PER_PERCENT = 80_000 * SCALING_FACTOR_1e18;

File 16 of 16 : enum.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

    enum MintAction {
        CLAIM
    }
    enum MintStatus {
        ACTIVE,
        CLAIMED
    }
    enum StakeAction {
        START,
        END
    }
    enum StakeStatus {
        ACTIVE,
        ENDED
    }
    enum PayoutTriggered {
        NO,
        YES
    }
    enum InitialLPMinted {
        NO,
        YES
    }
    enum PayoutClaim {
        SHARES
    }

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"genesisAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Legacy_AtLeastHalfMaturity","type":"error"},{"inputs":[],"name":"Legacy_EmptyUndistributeFees","type":"error"},{"inputs":[],"name":"Legacy_FailedToSendAmount","type":"error"},{"inputs":[],"name":"Legacy_InsufficientBalance","type":"error"},{"inputs":[],"name":"Legacy_InsufficientProtocolFees","type":"error"},{"inputs":[],"name":"Legacy_InvalidAddress","type":"error"},{"inputs":[],"name":"Legacy_InvalidBatchCount","type":"error"},{"inputs":[],"name":"Legacy_InvalidMintLength","type":"error"},{"inputs":[],"name":"Legacy_InvalidMintPower","type":"error"},{"inputs":[],"name":"Legacy_InvalidStakeLength","type":"error"},{"inputs":[],"name":"Legacy_LPTokensHasMinted","type":"error"},{"inputs":[],"name":"Legacy_MaxedWalletMints","type":"error"},{"inputs":[],"name":"Legacy_MaxedWalletStakes","type":"error"},{"inputs":[],"name":"Legacy_MintHasClaimed","type":"error"},{"inputs":[],"name":"Legacy_MintNotMature","type":"error"},{"inputs":[],"name":"Legacy_MintingPhaseFinished","type":"error"},{"inputs":[],"name":"Legacy_NoCycleRewardToClaim","type":"error"},{"inputs":[],"name":"Legacy_NoMintExists","type":"error"},{"inputs":[],"name":"Legacy_NoSharesExist","type":"error"},{"inputs":[],"name":"Legacy_NoStakeExists","type":"error"},{"inputs":[],"name":"Legacy_NotAllowed","type":"error"},{"inputs":[],"name":"Legacy_NotOnwer","type":"error"},{"inputs":[],"name":"Legacy_RequireOneMinimumShare","type":"error"},{"inputs":[],"name":"Legacy_StakeHasEnded","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"uint256","name":"cycleNo","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"CyclePayoutTriggered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ETHDistributed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"day","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"mintCost","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"shareRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mintableLegacy","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mintPowerBonus","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"EAABonus","type":"uint256"}],"name":"GlobalDailyDifficultyClockStats","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"lRank","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardMinted","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"penalty","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mintPenalty","type":"uint256"}],"name":"MintClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"lRank","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"gMintpower","type":"uint256"},{"components":[{"internalType":"uint8","name":"mintPower","type":"uint8"},{"internalType":"uint16","name":"numOfDays","type":"uint16"},{"internalType":"uint96","name":"mintableLegacy","type":"uint96"},{"internalType":"uint48","name":"mintStartTs","type":"uint48"},{"internalType":"uint48","name":"maturityTs","type":"uint48"},{"internalType":"uint32","name":"mintPowerBonus","type":"uint32"},{"internalType":"uint32","name":"EAABonus","type":"uint32"},{"internalType":"uint128","name":"mintedLegacy","type":"uint128"},{"internalType":"uint64","name":"mintCost","type":"uint64"},{"internalType":"enum MintStatus","name":"status","type":"uint8"}],"indexed":false,"internalType":"struct MintInfo.UserMintInfo","name":"userMintInfo","type":"tuple"}],"name":"MintStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"day","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ProtocolFeeReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"RewardClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"globalStakeId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"legacyAmount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"penalty","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"penaltyAmount","type":"uint256"}],"name":"StakeEnded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"globalStakeId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"numOfDays","type":"uint256"},{"components":[{"internalType":"uint152","name":"legacyAmount","type":"uint152"},{"internalType":"uint128","name":"shares","type":"uint128"},{"internalType":"uint16","name":"numOfDays","type":"uint16"},{"internalType":"uint48","name":"stakeStartTs","type":"uint48"},{"internalType":"uint48","name":"maturityTs","type":"uint48"},{"internalType":"enum StakeStatus","name":"status","type":"uint8"}],"indexed":true,"internalType":"struct StakeInfo.UserStakeInfo","name":"userStakeInfo","type":"tuple"}],"name":"StakeStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"batchClaimMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"mintPower","type":"uint256"},{"internalType":"uint256","name":"numOfDays","type":"uint256"},{"internalType":"uint256","name":"count","type":"uint256"}],"name":"batchMint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"burnLPTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"claimMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimUserAvailableETHPayouts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"distributeETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"endStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"genesisTs","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockTimeStamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentContractDay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"cycleNo","type":"uint256"}],"name":"getCurrentCycleIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentEAABonus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentMintCost","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentMintPowerBonus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentMintableLegacy","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentShareRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"cycleNo","type":"uint256"}],"name":"getCyclePayoutPool","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGlobalActiveShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGlobalActiveStakes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGlobalExpiredShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGlobalLRank","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGlobalMintPower","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGlobalPayoutTriggered","outputs":[{"internalType":"enum PayoutTriggered","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGlobalShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGlobalStakeId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"cycleNo","type":"uint256"}],"name":"getNextCyclePayoutDay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"cycleNo","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getPayoutPerShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTitanxDistributedBurnFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalActiveMints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalLegacyRewardsMinted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalLegacyStaked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalMintClaim","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalMintPenalty","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalMintedLegacy","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalMinting","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalPenalties","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalStakeEnd","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalStakePenalty","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUndistributedEth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserCurrentActiveShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserETHClaimableTotal","outputs":[{"internalType":"uint256","name":"reward","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"cycleNo","type":"uint256"}],"name":"getUserLastClaimIndex","outputs":[{"internalType":"uint256","name":"cycleIndex","type":"uint256"},{"internalType":"uint256","name":"sharesIndex","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserLatestMintId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserLatestShareIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getUserMintInfo","outputs":[{"components":[{"internalType":"uint8","name":"mintPower","type":"uint8"},{"internalType":"uint16","name":"numOfDays","type":"uint16"},{"internalType":"uint96","name":"mintableLegacy","type":"uint96"},{"internalType":"uint48","name":"mintStartTs","type":"uint48"},{"internalType":"uint48","name":"maturityTs","type":"uint48"},{"internalType":"uint32","name":"mintPowerBonus","type":"uint32"},{"internalType":"uint32","name":"EAABonus","type":"uint32"},{"internalType":"uint128","name":"mintedLegacy","type":"uint128"},{"internalType":"uint64","name":"mintCost","type":"uint64"},{"internalType":"enum MintStatus","name":"status","type":"uint8"}],"internalType":"struct MintInfo.UserMintInfo","name":"mintInfo","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserMints","outputs":[{"components":[{"internalType":"uint256","name":"mId","type":"uint256"},{"internalType":"uint256","name":"lRank","type":"uint256"},{"internalType":"uint256","name":"gMintPower","type":"uint256"},{"components":[{"internalType":"uint8","name":"mintPower","type":"uint8"},{"internalType":"uint16","name":"numOfDays","type":"uint16"},{"internalType":"uint96","name":"mintableLegacy","type":"uint96"},{"internalType":"uint48","name":"mintStartTs","type":"uint48"},{"internalType":"uint48","name":"maturityTs","type":"uint48"},{"internalType":"uint32","name":"mintPowerBonus","type":"uint32"},{"internalType":"uint32","name":"EAABonus","type":"uint32"},{"internalType":"uint128","name":"mintedLegacy","type":"uint128"},{"internalType":"uint64","name":"mintCost","type":"uint64"},{"internalType":"enum MintStatus","name":"status","type":"uint8"}],"internalType":"struct MintInfo.UserMintInfo","name":"mintInfo","type":"tuple"}],"internalType":"struct MintInfo.UserMint[]","name":"mintInfos","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getUserStakeInfo","outputs":[{"components":[{"internalType":"uint152","name":"legacyAmount","type":"uint152"},{"internalType":"uint128","name":"shares","type":"uint128"},{"internalType":"uint16","name":"numOfDays","type":"uint16"},{"internalType":"uint48","name":"stakeStartTs","type":"uint48"},{"internalType":"uint48","name":"maturityTs","type":"uint48"},{"internalType":"enum StakeStatus","name":"status","type":"uint8"}],"internalType":"struct StakeInfo.UserStakeInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserStakes","outputs":[{"components":[{"internalType":"uint256","name":"sId","type":"uint256"},{"internalType":"uint256","name":"globalStakeId","type":"uint256"},{"components":[{"internalType":"uint152","name":"legacyAmount","type":"uint152"},{"internalType":"uint128","name":"shares","type":"uint128"},{"internalType":"uint16","name":"numOfDays","type":"uint16"},{"internalType":"uint48","name":"stakeStartTs","type":"uint48"},{"internalType":"uint48","name":"maturityTs","type":"uint48"},{"internalType":"enum StakeStatus","name":"status","type":"uint8"}],"internalType":"struct StakeInfo.UserStakeInfo","name":"stakeInfo","type":"tuple"}],"internalType":"struct StakeInfo.UserStake[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"manualDailyDifficultyClock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mintLPTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"contractAddress","type":"address"}],"name":"setBuyAndBurnContractAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAddress","type":"address"}],"name":"setNewGenesisAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"contractAddress","type":"address"}],"name":"setTitanXBuyAndBurnContractAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"mintPower","type":"uint256"},{"internalType":"uint256","name":"numOfDays","type":"uint256"}],"name":"startMint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"numOfDays","type":"uint256"}],"name":"startStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"triggerPayouts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60a06040523480156200001157600080fd5b5060405162004f7f38038062004f7f8339810160408190526200003491620002e9565b604051806040016040528060068152602001654c656761637960d01b815250604051806040016040528060068152602001654c454741435960d01b81525081600390805190602001906200008a92919062000243565b508051620000a090600490602084019062000243565b5050600160058190554260805260065550600780546001600160e81b03191679056bc75e2d6310000002c68af0bb1400002b5e3af16b18800000179055620000ed620f424060036200031b565b6008805463ffffffff191663ffffffff9290921691909117905562000117620f4240600a6200031b565b6008805463ffffffff929092166401000000000263ffffffff60201b19909216919091179055600d602052601c7fe08c955e5efff0d87732e6655002ef18ef6cad920399de609dbb470f9b22665f55605a7f6d7a92e696f0688f26e4b1ea6039fe4448d8a822e67ac794a76d0e65df6db552556101717fe1718144cbeaa1a32a28fee01ecb62ad9d07562e126ff79623a9a26eda05ca475561037860008190527ff22d066e86e14b611c2c6318708f35a95ea9f6a2107f6122a10ad3badf8d4d0655620001e13390565b602280546001600160a01b0319166001600160a01b0392831617905581166200021d57604051637893a68160e01b815260040160405180910390fd5b602380546001600160a01b0319166001600160a01b039290921691909117905562000386565b828054620002519062000349565b90600052602060002090601f016020900481019282620002755760008555620002c0565b82601f106200029057805160ff1916838001178555620002c0565b82800160010185558215620002c0579182015b82811115620002c0578251825591602001919060010190620002a3565b50620002ce929150620002d2565b5090565b5b80821115620002ce5760008155600101620002d3565b600060208284031215620002fc57600080fd5b81516001600160a01b03811681146200031457600080fd5b9392505050565b60008160001904831182151516156200034457634e487b7160e01b600052601160045260246000fd5b500290565b600181811c908216806200035e57607f821691505b602082108114156200038057634e487b7160e01b600052602260045260246000fd5b50919050565b608051614bd6620003a960003960008181610bf40152611dd30152614bd66000f3fe6080604052600436106104075760003560e01c80637d6b325311610213578063baf20eef11610123578063e33a3c94116100ab578063f63ec50e1161007a578063f63ec50e14610c4d578063f80b0cfb14610c68578063faa94d3b14610c7d578063fbf9529d14610cb2578063ffb75cab14610cd257600080fd5b8063e33a3c9414610bc5578063e3af6d0a14610be5578063e3d3227d14610c18578063f2fde38b14610c2d57600080fd5b8063d09a2dda116100f2578063d09a2dda14610b3b578063d819e19814610b50578063d9af94af14610b7d578063dd62ed3e14610b90578063dff96e9a14610bb057600080fd5b8063baf20eef14610abb578063bb88603c14610adb578063c081f4c014610af0578063c50312ad14610b0557600080fd5b806397932d07116101a6578063af4fb76311610175578063af4fb76314610a3a578063af835b8a14610a4f578063b8b9b54914610a7c578063b8fac78914610a91578063b984c94614610aa657600080fd5b806397932d071461099b5780639a5a6cd9146109b0578063a457c2d7146109fa578063a9059cbb14610a1a57600080fd5b806389de4165116101e257806389de41651461092a57806392c1df541461094a578063933281241461095f57806395d89b411461098657600080fd5b80637d6b3253146108b3578063800bb269146108d3578063842e2981146108e8578063880a08361461091557600080fd5b8063313ce56711610319578063635d70f4116102a157806370a082311161027057806370a08231146107ea57806370c9b00214610820578063715018a6146108745780637291fb16146108895780637789281e1461089e57600080fd5b8063635d70f4146107885780636c52876b1461079b5780636e83dc73146107b05780636f609633146107c557600080fd5b8063462a8c2f116102e8578063462a8c2f146107075780634b726c161461073457806350a3a907146107495780635fc768481461075e57806361eb8d011461077357600080fd5b8063313ce5671461069657806339509351146106b25780633a9693e1146106d25780633dda7881146106f257600080fd5b806318160ddd1161039c578063216630b41161036b578063216630b41461060a5780632277d1bd1461061f578063236393851461063457806323b872dd146106495780632d02347a1461066957600080fd5b806318160ddd146105ad5780631851b8bd146105c25780631ae409c0146105e25780631fd979e0146105f757600080fd5b80630cbe28d6116103d85780630cbe28d61461053a5780630fe757c81461055c57806312065fe01461057a578063128bfcae1461058d57600080fd5b8062281d141461048a578062ae5faa146104bb57806306fdde03146104e8578063095ea7b31461050a57600080fd5b36610485576027805434919060009061042a9084906001600160581b0316614611565b92506101000a8154816001600160581b0302191690836001600160581b031602179055503461045860065490565b60405133907fa74c7a39bcbc9995bdd408cb2bef293a21224b85e69d60b9f008c9abbcbff1a390600090a4005b600080fd5b34801561049657600080fd5b50600854600160201b900463ffffffff165b6040519081526020015b60405180910390f35b3480156104c757600080fd5b506104db6104d6366004614658565b610cff565b6040516104b2919061471f565b3480156104f457600080fd5b506104fd610dd6565b6040516104b2919061472d565b34801561051657600080fd5b5061052a610525366004614658565b610e68565b60405190151581526020016104b2565b34801561054657600080fd5b5061055a610555366004614782565b610e80565b005b34801561056857600080fd5b506027546001600160581b03166104a8565b34801561058657600080fd5b50476104a8565b34801561059957600080fd5b5061055a6105a836600461479b565b610ec9565b3480156105b957600080fd5b506002546104a8565b3480156105ce57600080fd5b5061055a6105dd3660046147bd565b610f52565b3480156105ee57600080fd5b506006546104a8565b61055a6106053660046147d8565b610fa3565b34801561061657600080fd5b506011546104a8565b34801561062b57600080fd5b5061055a6110a7565b34801561064057600080fd5b506104a86112de565b34801561065557600080fd5b5061052a610664366004614804565b6112f5565b34801561067557600080fd5b506104a8610684366004614782565b6000908152600d602052604090205490565b3480156106a257600080fd5b50604051601281526020016104b2565b3480156106be57600080fd5b5061052a6106cd366004614658565b61131b565b3480156106de57600080fd5b5061055a6106ed3660046147bd565b61133d565b3480156106fe57600080fd5b5061055a6113ba565b34801561071357600080fd5b506104a8610722366004614782565b60009081526009602052604090205490565b34801561074057600080fd5b506026546104a8565b34801561075557600080fd5b506104a8611485565b34801561076a57600080fd5b506010546104a8565b34801561077f57600080fd5b50601a546104a8565b61055a61079636600461479b565b61149b565b3480156107a757600080fd5b506104a86115c4565b3480156107bc57600080fd5b50600e546104a8565b3480156107d157600080fd5b50600754600160481b90046001600160401b03166104a8565b3480156107f657600080fd5b506104a86108053660046147bd565b6001600160a01b031660009081526020819052604090205490565b34801561082c57600080fd5b5061085f61083b36600461479b565b6000918252600b602090815260408084209284529190529020600181015490549091565b604080519283526020830191909152016104b2565b34801561088057600080fd5b5061055a6115dc565b34801561089557600080fd5b5061055a6115ee565b3480156108aa57600080fd5b506019546104a8565b3480156108bf57600080fd5b5061055a6108ce3660046147bd565b611619565b3480156108df57600080fd5b506104a861166a565b3480156108f457600080fd5b506109086109033660046147bd565b611682565b6040516104b29190614840565b34801561092157600080fd5b506018546104a8565b34801561093657600080fd5b5061085f610945366004614658565b61178a565b34801561095657600080fd5b506013546104a8565b34801561096b57600080fd5b50600854600160401b900460ff166040516104b291906148a4565b34801561099257600080fd5b506104fd6117ce565b3480156109a757600080fd5b506104a86117dd565b3480156109bc57600080fd5b506104a86109cb3660046147bd565b6001600160a01b0316600090815260216020908152604080832082805281842054845290915290206001015490565b348015610a0657600080fd5b5061052a610a15366004614658565b6117e8565b348015610a2657600080fd5b5061052a610a35366004614658565b611873565b348015610a4657600080fd5b506104a8611881565b348015610a5b57600080fd5b506104a8610a6a366004614782565b6000908152600a602052604090205490565b348015610a8857600080fd5b5061055a611893565b348015610a9d57600080fd5b50601c546104a8565b348015610ab257600080fd5b506017546104a8565b348015610ac757600080fd5b5061055a610ad6366004614782565b6118d3565b348015610ae757600080fd5b5061055a6118f2565b348015610afc57600080fd5b50601b546104a8565b348015610b1157600080fd5b506104a8610b203660046147bd565b6001600160a01b031660009081526014602052604090205490565b348015610b4757600080fd5b5061055a61191f565b348015610b5c57600080fd5b50610b70610b6b366004614658565b611927565b6040516104b291906149a2565b348015610b8957600080fd5b50426104a8565b348015610b9c57600080fd5b506104a8610bab3660046149b1565b611a2d565b348015610bbc57600080fd5b506012546104a8565b348015610bd157600080fd5b506104a8610be03660046147bd565b611a58565b348015610bf157600080fd5b507f00000000000000000000000000000000000000000000000000000000000000006104a8565b348015610c2457600080fd5b5061055a611ad7565b348015610c3957600080fd5b5061055a610c483660046147bd565b611b7f565b348015610c5957600080fd5b5060085463ffffffff166104a8565b348015610c7457600080fd5b50600f546104a8565b348015610c8957600080fd5b506104a8610c983660046147bd565b6001600160a01b0316600090815260208052604090205490565b348015610cbe57600080fd5b5060075468ffffffffffffffffff166104a8565b348015610cde57600080fd5b50610cf2610ced3660046147bd565b611b90565b6040516104b291906149e4565b610d0761452a565b6001600160a01b0383166000908152601e602090815260408083208584528252808320548352601f825291829020825160c08101845281546001600160981b031681526001808301546001600160801b03811694830194909452600160801b840461ffff1694820194909452600160901b830465ffffffffffff9081166060830152600160c01b840416608082015292909160a0840191600160f01b90910460ff1690811115610db957610db9614682565b6001811115610dca57610dca614682565b90525090505b92915050565b606060038054610de590614a48565b80601f0160208091040260200160405190810160405280929190818152602001828054610e1190614a48565b8015610e5e5780601f10610e3357610100808354040283529160200191610e5e565b820191906000526020600020905b815481529060010190602001808311610e4157829003601f168201915b5050505050905090565b600033610e76818585611ca1565b5060019392505050565b610e88611dc5565b610e906120b6565b610ebc33610eb73384610ea260065490565b600854600190600160401b900460ff16612110565b6122c9565b610ec66001600555565b50565b610ed1611dc5565b610ed96120b6565b81610ee333610805565b1015610f025760405163122cb54d60e21b815260040160405180910390fd5b610f0c3383612388565b610f4433610f3f338585610f2a60075468ffffffffffffffffff1690565b600654600854600160401b900460ff166124ba565b6127b0565b610f4e6001600555565b5050565b610f5a6129d2565b6001600160a01b038116610f8157604051637893a68160e01b815260040160405180910390fd5b602580546001600160a01b0319166001600160a01b0392909216919091179055565b610fab6120b6565b610fb3611dc5565b801580610fc05750606481115b15610fde5760405163819d482b60e01b815260040160405180910390fd5b6103e881610feb33610b20565b610ff59190614a7d565b111561101457604051630c48402960e31b815260040160405180910390fd5b600061101e611485565b1161103c57604051634953318160e11b815260040160405180910390fd5b61108e33848461104a611485565b60085463ffffffff16600854600160201b900463ffffffff16876110898b60016110846007546001600160401b03600160481b9091041690565b6129fd565b612a28565b6110988382612aa4565b6110a26001600555565b505050565b6110af611dc5565b6110b76120b6565b60006110c260195490565b6018546110cf9190614a95565b905060018110156110f3576040516302f86e9360e61b815260040160405180910390fd5b6027546000908190819081906001600160581b03161561112057611115612b83565b929650909450925090505b600061112b60065490565b90506000600161113d601c8985612d63565b600181111561114e5761114e614682565b14801561116c5750600081600181111561116a5761116a614682565b145b611176578061117b565b506001805b50600161118a605a8985612d63565b600181111561119b5761119b614682565b1480156111b9575060008160018111156111b7576111b7614682565b145b6111c357806111c8565b506001805b5060016111d86101718985612d63565b60018111156111e9576111e9614682565b1480156112075750600081600181111561120557611205614682565b145b6112115780611216565b506001805b5060016112266103788985612d63565b600181111561123757611237614682565b1480156112555750600081600181111561125357611253614682565b145b61125f5780611264565b506001805b50600181600181111561127957611279614682565b14156112b9576000600854600160401b900460ff16600181111561129f5761129f614682565b14156112b9576008805460ff60401b1916600160401b1790555b85156112cb576112cb86868686612dee565b505050505050506112dc6001600555565b565b6000600f54600e546112f09190614a95565b905090565b600033611303858285612e67565b61130e858585612edb565b60019150505b9392505050565b600033610e7681858561132e8383611a2d565b6113389190614a7d565b611ca1565b6023546001600160a01b0316336001600160a01b03161461137157604051632409f4d960e11b815260040160405180910390fd5b6001600160a01b03811661139857604051637893a68160e01b815260040160405180910390fd5b602380546001600160a01b0319166001600160a01b0392909216919091179055565b6113c2611dc5565b6113ca6120b6565b60006113d8601c600061307f565b90506113e6605a600061307f565b6113f09082614a7d565b90506113ff610171600061307f565b6114099082614a7d565b9050611418610378600061307f565b6114229082614a7d565b9050806114425760405163046f909560e11b815260040160405180910390fd5b61144d335b826130c3565b604051819033907f106f923f993c2149d49b4255ff723acafa1f2d94393f561d3eda32ae348f724190600090a3506112dc6001600555565b600754600160881b90046001600160601b031690565b6114a36120b6565b6114ab611dc5565b6103e86114b733610b20565b6114c2906001614a7d565b11156114e157604051630c48402960e31b815260040160405180910390fd5b60006114eb611485565b1161150957604051634953318160e11b815260040160405180910390fd5b60008261151560135490565b61151f9190614a7d565b9050600061152c600e5490565b611537906001614a7d565b90506000611589338686611549611485565b60085463ffffffff16600854600160201b900463ffffffff1689896115848e60016110846007546001600160401b03600160481b9091041690565b61315e565b6011546115969190614a7d565b90506115ac828483600e92909255601355601155565b6115b7856001612aa4565b505050610f4e6001600555565b60006115cf601b5490565b6012546112f09190614a7d565b6115e46129d2565b6112dc6000613482565b6115f6611dc5565b6115fe6120b6565b61160f61160a336134a4565b6136e5565b6112dc6001600555565b6116216129d2565b6001600160a01b03811661164857604051637893a68160e01b815260040160405180910390fd5b602480546001600160a01b0319166001600160a01b0392909216919091179055565b6000611675601c5490565b6017546112f09190614a95565b6001600160a01b0381166000908152601d6020526040812054606091816001600160401b038111156116b6576116b6614aac565b6040519080825280602002602001820160405280156116ef57816020015b6116dc614560565b8152602001906001900390816116d45790505b50905060015b82811161178257604080516060810182528281526001600160a01b0387166000908152601e6020908152838220858352815290839020546001600160801b0316908201529081016117468784610cff565b905282611754600184614a95565b8151811061176457611764614ac2565b6020026020010181905250808061177a90614ad8565b9150506116f5565b509392505050565b6001600160a01b03919091166000908152600c6020908152604080832093835292905220546001600160601b03811691600160601b9091046001600160401b031690565b606060048054610de590614a48565b60006112f060105490565b600033816117f68286611a2d565b90508381101561185b5760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084015b60405180910390fd5b6118688286868403611ca1565b506001949350505050565b600033610e76818585612edb565b60006019546018546112f09190614a95565b61189b611dc5565b6118a36120b6565b6000806000806118b1612b83565b93509350935093506118c584848484612dee565b505050506112dc6001600555565b6118db611dc5565b6118e36120b6565b610ebc61160a33836000613718565b6118fa611dc5565b6024546001600160a01b03166000818152602081905260409020546112dc9190612388565b6112dc611dc5565b61192f614581565b6001600160a01b038316600090815260156020908152604080832085845282528083205483526016825291829020825161014081018452815460ff808216835261010080830461ffff1695840195909552630100000082046001600160601b031695830195909552600160781b810465ffffffffffff9081166060840152600160a81b8204166080830152600160d81b900463ffffffff90811660a083015260018084015491821660c0840152600160201b82046001600160801b031660e0840152600160a01b82046001600160401b03169483019490945290939192610120850192600160e01b90920490911690811115610db957610db9614682565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b600080611a6883601c6000613938565b50909150611a7890508183614a7d565b9150611a8783605a6000613938565b50909150611a9790508183614a7d565b9150611aa7836101716000613938565b50909150611ab790508183614a7d565b9150611ac7836103786000613938565b5090915061131490508183614a7d565b6024546001600160a01b0316336001600160a01b031614611b0b57604051632409f4d960e11b815260040160405180910390fd5b6001602754600160581b900460ff166001811115611b2b57611b2b614682565b1415611b4a57604051631e9abe7f60e01b815260040160405180910390fd5b6027805460ff60581b1916600160581b1790556024546112dc906001600160a01b03166b0c1ded63574de0e4660000006122c9565b611b876129d2565b610ec681613482565b6001600160a01b038116600090815260146020526040902054606090806001600160401b03811115611bc457611bc4614aac565b604051908082528060200260200182016040528015611bfd57816020015b611bea6145d3565b815260200190600190039081611be25790505b50915060015b818111611c9a57604080516080810182528281526001600160a01b038616600090815260156020908152838220858352808252848320805483860152928690529052600101549181019190915260608101611c5e8684611927565b905283611c6c600184614a95565b81518110611c7c57611c7c614ac2565b60200260200101819052508080611c9290614ad8565b915050611c03565b5050919050565b6001600160a01b038316611d035760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401611852565b6001600160a01b038216611d645760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401611852565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b600654600062015180611df87f000000000000000000000000000000000000000000000000000000000000000042614a95565b611e029190614af3565b611e0d906001614a7d565b905081811115610f4e57600754600854600160481b82046001600160401b03169168ffffffffffffffffff811691600160881b9091046001600160601b03169063ffffffff80821691600160201b9004166000611e6a8888614a95565b905060005b8181101561200357612710611e8661271889614b15565b611e909190614af3565b9650612710611ea161271388614b15565b611eab9190614af3565b9550612710611ebc6126ed87614b15565b611ec69190614af3565b9450612710611ed76126ed86614b15565b611ee19190614af3565b9350670de0b6b3a7640000871115611eff57670de0b6b3a764000096505b6897c9ce4cf6d5c00000861115611f1d576897c9ce4cf6d5c0000095505b670de0b6b3a7640000851015611f5b576a115eec47f6cf7e35000000611f416117dd565b10611f4f5760009450611f5b565b670de0b6b3a764000094505b611f686103e86003614b15565b841015611f7f57611f7c6103e86003614b15565b93505b61015e8811611f9b57611f94616f9b84614a95565b9250611fa0565b600092505b8587611fab8b614ad8565b6040805189815260208101899052908101879052909b508b907fa9283897e800a6f72762253d689720a86b1cc3604e78a9b6ee580413ea30a9539060600160405180910390a480611ffb81614ad8565b915050611e6f565b50600780546001600160601b038616600160881b027fffffff000000000000000000000000ffffffffffffffff0000000000000000006001600160401b038a16600160481b02166001600160e81b03199092169190911768ffffffffffffffffff8816171790556008805463ffffffff848116600160201b0267ffffffffffffffff19909216908616171780825560068990556000919060ff60401b1916600160401b8302179055505050505050505050565b600260055414156121095760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401611852565b6002600555565b6001600160a01b0385166000908152601e60209081526040808320878452909152812054806121525760405163143332f360e11b815260040160405180910390fd5b6000818152601f60209081526040808320815160c08101835281546001600160981b031681526001808301546001600160801b0381169583019590955261ffff600160801b8604169382019390935265ffffffffffff600160901b850481166060830152600160c01b850416608082015292909160a084019160ff600160f01b90910416908111156121e6576121e6614682565b60018111156121f7576121f7614682565b905250905060018160a00151600181111561221457612214614682565b1415612233576040516379776e4760e01b815260040160405180910390fd5b600081602001516001600160801b03169050612260898284600001516001600160981b03168a898b613ab2565b50600186600181111561227557612275614682565b14156122b157601c6000815461228a90614ad8565b909155506000838152601f60205260409020600101805460ff60f01b1916600160f01b1790555b6122bc898484613c64565b9998505050505050505050565b6001600160a01b03821661231f5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401611852565b80600260008282546123319190614a7d565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b6001600160a01b0382166123e85760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401611852565b6001600160a01b0382166000908152602081905260409020548181101561245c5760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401611852565b6001600160a01b0383166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b6001600160a01b0386166000908152601d60205260408120805482919082906124e290614ad8565b918290555090506103e881111561250c576040516372f4ca1560e01b815260040160405180910390fd5b601c86108061251c5750610dac86115b1561253a5760405163689b275f60e01b815260040160405180910390fd5b6000612547888888613d2d565b9050600161255d670de0b6b3a764000083614af3565b101561257c5760405163758012ff60e11b815260040160405180910390fd5b600060176000815461258d90614ad8565b9182905550905060006125a3620151808a614b15565b6125ad9042614a7d565b905060006040518060c001604052808c6001600160981b03168152602001856001600160801b031681526020018b61ffff1681526020014265ffffffffffff1681526020018365ffffffffffff1681526020016000600181111561261357612613614682565b90526001600160a01b038d166000908152601e602090815260408083208984528252808320879055868352601f8252918290208351815472ffffffffffffffffffffffffffffffffffffff19166001600160981b03909116178155908301516001808301805494860151606087015160808801516001600160801b0390951671ffffffffffffffffffffffffffffffffffff1990971696909617600160801b61ffff90921691909102176bffffffffffffffffffffffff60901b1916600160901b65ffffffffffff9687160265ffffffffffff60c01b191617600160c01b95909316949094029190911780845560a0850151949550859492939160ff60f01b1990911690600160f01b90849081111561272e5761272e614682565b02179055509050506127458c858d8b8b6000613ab2565b9550806040516127559190614b34565b6040518091039020838d6001600160a01b03167fecd17a550d3024bd4dcec573e568e747e7843155893d1926213c848215a0d0298d60405161279991815260200190565b60405180910390a450505050509695505050505050565b8060011415610f4e57601c600052600a6020527f964ea767231031507a3f70c59b06c72a2054875e2bc2938da2a55d8f6cb774eb5415610f4e57601c600052600a6020527f964ea767231031507a3f70c59b06c72a2054875e2bc2938da2a55d8f6cb774eb54612821906001614a7d565b6001600160a01b0383166000908152600c60209081526040808320601c84528252822080546001600160601b0319166001600160601b039490941693909317909255605a9052600a90527f7f87218992b43f7ec59f3c8fd242b6759bfedfc613fdc2676bc53b4637f8f35154612898906001614a7d565b6001600160a01b0383166000908152600c60209081526040808320605a84528252822080546001600160601b0319166001600160601b0394909416939093179092556101719052600a90527fb03a258bbb90d8d1843170969b808b3100da20cb067e31b0b691b6f43141902e54612910906001614a7d565b6001600160a01b0383166000908152600c6020908152604080832061017184528252822080546001600160601b0319166001600160601b0394909416939093179092556103789052600a90527fb65719cf4862d40ddcfbadca8d587b82e645261e95d3c4e28fef5a0d6eefb6d654612989906001614a7d565b6001600160a01b0383166000908152600c602090815260408083206103788452909152902080546001600160601b03929092166001600160601b03199092169190911790555050565b6022546001600160a01b031633146112dc576040516341ae395f60e01b815260040160405180910390fd5b6000606483612a0c8685614b15565b612a169190614b15565b612a209190614af3565b949350505050565b601354600e5460115460005b85811015612a8257612a468b85614a7d565b9350612a648c8c8c8c8c8c8a612a5b8b614ad8565b9a508a8d61315e565b612a6e9083614a7d565b915080612a7a81614ad8565b915050612a34565b50612a97828483600e92909255601355601155565b5050505050505050505050565b6000612ac583836110846007546001600160401b03600160481b9091041690565b905080341015612ae85760405163620b518b60e01b815260040160405180910390fd5b6027805460009183918390612b079084906001600160581b0316614611565b92506101000a8154816001600160581b0302191690836001600160581b031602179055508134612b379190614a95565b90508015612b4857612b4833611447565b81612b5260065490565b60405133907fa74c7a39bcbc9995bdd408cb2bef293a21224b85e69d60b9f008c9abbcbff1a390600090a450505050565b6027546000908190819081906001600160581b031680612bb657604051634e63e78360e01b815260040160405180910390fd5b602780546affffffffffffffffffffff19169055604051819033907f55083a582b32208b745a21c8ce4f8d545be8cce1437f34637f08fc9d943eacb090600090a3620f4240612c076119c883614b15565b612c119190614af3565b9450612c1d8582614a95565b905060009350600092506000612c31611485565b1115612c6e57612710612c4661083483614b15565b612c509190614af3565b9350612710612c616103e883614b15565b612c6b9190614af3565b92505b612710612c7d61019083614b15565b612c879190614af3565b915060008284612c978785614a95565b612ca19190614a95565b612cab9190614a95565b90508015612d5b576000612710612cc461096084614b15565b612cce9190614af3565b90506000612710612ce1610d4885614b15565b612ceb9190614af3565b90506000612710612cfe610bb886614b15565b612d089190614af3565b9050612d15601c84613d85565b612d20605a83613d85565b612d2c61017182613d85565b612d576103788284612d3e8789614a95565b612d489190614a95565b612d529190614a95565b613d85565b5050505b505090919293565b6000838152600d6020526040812054821015612d8157506000611314565b612d8a84613dac565b60008481526009602052604090205480612da8576000915050611314565b612db3858286613e17565b506040518190869033907f973833b060ae272a085ff5b17b764e39ddac6a5612bdc9337f586dc7efee604990600090a4506001949350505050565b612df833856130c3565b602354612e0e906001600160a01b0316826130c3565b6000612e18611485565b1115612e6157602454612e34906001600160a01b0316846130c3565b8160266000828254612e469190614a7d565b9091555050602554612e61906001600160a01b0316836130c3565b50505050565b6000612e738484611a2d565b90506000198114612e615781811015612ece5760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401611852565b612e618484848403611ca1565b6001600160a01b038316612f3f5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401611852565b6001600160a01b038216612fa15760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401611852565b6001600160a01b038316600090815260208190526040902054818110156130195760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401611852565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3612e61565b600080808061308f338787613938565b9194509250905060008580156130a7576130a7614682565b14156130b9576130b933878484613e92565b5090949350505050565b6001600160a01b0382166130ea57604051637893a68160e01b815260040160405180910390fd5b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114613137576040519150601f19603f3d011682016040523d82523d6000602084013e61313c565b606091505b50509050806110a25760405163297a66e760e11b815260040160405180910390fd5b600087158061316e575061011888115b1561318c57604051635a2fc35d60e01b815260040160405180910390fd5b8815806131995750606489115b156131b757604051637856200960e11b815260040160405180910390fd5b6131c389898988613f8a565b905060006040518061014001604052808b60ff1681526020018a61ffff168152602001836001600160601b031681526020014265ffffffffffff168152602001620151808b6132129190614b15565b61321c9042614a7d565b65ffffffffffff16815263ffffffff808a166020830152881660408201526000606082018190526001600160401b038616608083015260a0909101526001600160a01b038c16600090815260146020526040812080549293509091829061328290614ad8565b91829055506001600160a01b038d166000908152601560209081526040808320848452825280832089815560019081018b90558984526016835292819020865181549388015192880151606089015160808a015160a08b015160ff90941662ffffff199097169690961761010061ffff90961686021774ffffffffffffffffffffffffffffffffffff000000191663010000006001600160601b039093169290920265ffffffffffff60781b191691909117600160781b65ffffffffffff928316021769ffffffffffffffffffff60a81b1916600160a81b919095160263ffffffff60d81b191693909317600160d81b63ffffffff9485160217815560c0870151818501805460e08a0151948a0151929095166001600160a01b031990951694909417600160201b6001600160801b03909416939093029290921767ffffffffffffffff60a01b198116600160a01b6001600160401b039094169390930292831784556101208801519596508795919491939268ffffffffffffffffff60a01b1990911660ff60e01b199091161790600160e01b90849081111561342857613428614682565b021790555090505085858d6001600160a01b03167f2109b8587b0ddbd9adf8ec24ce76bef548f2aee7aac34bc6aa0bb51b7cba9d678560405161346b91906149a2565b60405180910390a450509998505050505050505050565b602280546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0381166000908152601460205260408120548180806134c8614581565b60015b8581116136a9576001600160a01b03881660009081526015602090815260408083208484528252808320548084526016835292819020815161014081018352815460ff808216835261010080830461ffff1696840196909652630100000082046001600160601b031694830194909452600160781b810465ffffffffffff9081166060840152600160a81b8204166080830152600160d81b900463ffffffff90811660a083015260018084015491821660c0840152600160201b82046001600160801b031660e0840152600160a01b82046001600160401b031695830195909552949850939092610120850192600160e01b909204909116908111156135d3576135d3614682565b60018111156135e4576135e4614682565b90525091506000826101200151600181111561360257613602614682565b14801561361b5750816080015165ffffffffffff164210155b15613689576001600160a01b0388166000908152601560209081526040808320848452909152812060010154613656918a9187918690614035565b6136609088614a7d565b965081604001516001600160601b03168361367b9190614a7d565b925061368685614ad8565b94505b8460641415613697576136a9565b806136a181614ad8565b9150506134cb565b5081601160008282546136bc9190614a95565b9250508190555081601060008282546136d59190614a7d565b9091555095979650505050505050565b6136ef33826122c9565b602354610ec6906001600160a01b031661271061370e846101f4614b15565b610eb79190614af3565b6001600160a01b0383166000908152601560209081526040808320858452909152812080546001909101548161376157604051634e32f3a360e11b815260040160405180910390fd5b6000828152601660209081526040808320815161014081018352815460ff808216835261010080830461ffff1696840196909652630100000082046001600160601b031694830194909452600160781b810465ffffffffffff9081166060840152600160a81b8204166080830152600160d81b900463ffffffff90811660a083015260018084015491821660c0840152600160201b82046001600160801b031660e0840152600160a01b82046001600160401b03169583019590955290939192610120850192600160e01b909204169081111561384057613840614682565b600181111561385157613851614682565b90525090506001816101200151600181111561386f5761386f614682565b141561388e576040516399adb6e160e01b815260040160405180910390fd5b42816080015165ffffffffffff161180156138b7575060008580156138b5576138b5614682565b145b156138d5576040516311023b4960e31b815260040160405180910390fd5b80604001516001600160601b0316601160008282546138f49190614a95565b9250508190555080604001516001600160601b03166010600082825461391a9190614a7d565b9091555061392d90508784848489614035565b979650505050505050565b600080600080613954866000908152600a602052604090205490565b9050600085801561396757613967614682565b1415613aa857613977878761178a565b9093509150600061399c886001600160a01b0316600090815260208052604090205490565b9050835b828111613aa5576000888152600b602090815260408083208484529091528120600181015490549091865b858111613a44576001600160a01b038d1660009081526021602090815260408083208484529091529020548310613a2a576001600160a01b038d1660009081526021602090815260408083208484529091529020600101549150613a2f565b613a44565b96508680613a3c81614ad8565b9150506139cb565b508215801590613a5357508015155b15613a8257670de0b6b3a7640000613a6b8483614b15565b613a759190614af3565b613a7f908a614a7d565b98505b613a8d846001614a7d565b97505050508080613a9d90614ad8565b9150506139a0565b50505b5093509350939050565b6001600160a01b038616600090815260208080526040808320546021835281842081855290925282206001015482846001811115613af257613af2614682565b1415613b7c5781613b0257600192505b613b0c8882614a7d565b6001600160a01b038a16600090815260216020526040812090613b2e85614ad8565b9450848152602001908152602001600020600101819055508760186000828254613b589190614a7d565b9250508190555086601a6000828254613b719190614a7d565b90915550613bf19050565b613b868882614a95565b6001600160a01b038a16600090815260216020526040812090613ba885614ad8565b9450848152602001908152602001600020600101819055508760196000828254613bd29190614a7d565b9250508190555086601a6000828254613beb9190614a95565b90915550505b6000856001811115613c0557613c05614682565b14613c1a57613c15866001614a7d565b613c1c565b855b6001600160a01b03909916600081815260216020908152604080832086845282528083206001600160801b039d909d16909c5591815290805298909820559695505050505050565b60008082600001516001600160981b031690506000613c9c846060015165ffffffffffff16856080015165ffffffffffff1642614228565b905060006064613cac8385614b15565b613cb69190614af3565b9050613cc28184614a95565b935080601b6000828254613cd69190614a7d565b90915550506040805185815260208101839052839188916001600160a01b038b16917f971d9ff3287b3ba75194105e7281e55c93b0a89cad9915664bb3fd9211f8d5f1910160405180910390a45050509392505050565b60008364174876e800613d4082866142ee565b613d4a9083614b15565b613d549190614af3565b613d5e9082614a7d565b9050613d72670de0b6b3a764000084614af3565b613d7c9082614af3565b95945050505050565b60008281526009602052604081208054839290613da3908490614a7d565b90915550505050565b6000818152600d60205260409020546006548181106110a25782613dd08383614a95565b613dda9190614af3565b613de5906001614a7d565b613def9084614b15565b6000848152600d602052604081208054909190613e0d908490614a7d565b9091555050505050565b6000838152600960209081526040808320839055600a909152812080548290613e3f90614ad8565b9182905550905081613e59670de0b6b3a764000085614b15565b613e639190614af3565b6000948552600b6020908152604080872084885290915290942060018101949094556006549093555090919050565b6001600160a01b0384166000908152600c602090815260408083208684529091529020546001600160601b03168214613f02576001600160a01b0384166000908152600c60209081526040808320868452909152902080546001600160601b0319166001600160601b0384161790555b6001600160a01b0384166000908152600c60209081526040808320868452909152902054600160601b90046001600160401b03168114612e61576001600160a01b0384166000908152600c60209081526040808320868452909152902080546001600160401b038316600160601b0267ffffffffffffffff60601b1990911617905550505050565b60008084613f988786614b15565b613fa29190614b15565b905084600114613fe657612710613fba600187614a95565b613fc5600b84614b15565b613fcf9190614b15565b613fd99190614af3565b613fe39082614a95565b90505b905080821561402057620f42406064613fff8584614b15565b6140099190614af3565b6140139190614af3565b61401d9083614a7d565b91505b61402b606483614af3565b9695505050505050565b60008082801561404757614047614682565b1415614070576000858152601660205260409020600101805460ff60e01b1916600160e01b1790555b6000806000856080015165ffffffffffff164211156140ac576140a9866080015165ffffffffffff16426140a49190614a95565b614397565b91505b60008580156140bd576140bd614682565b14156140e5576140e28660a0015163ffffffff16876000015160ff16896013546144ad565b90505b6140f2620f424082614af3565b86604001516001600160601b031661410a9190614a7d565b935060646141188386614b15565b6141229190614af3565b925061412e8385614a95565b9350600085801561414157614141614682565b141561415b57600f6000815461415690614ad8565b909155505b82156141795782601260008282546141739190614a7d565b90915550505b600085801561418a5761418a614682565b14156141ce576000888152601660205260409020600101805473ffffffffffffffffffffffffffffffff000000001916600160201b6001600160801b038716021790555b81888a6001600160a01b03167fbd866a3fbf35e201f790e87581b1afbb3165e879df5d35313a4875a70b9f3b368787604051614214929190918252602082015260400190565b60405180910390a450505095945050505050565b6000828211156142a457600061423e8484614a95565b90506000614250620151806007614b15565b905080821161426457600092505050611314565b61429b6201518061427f6142788486614a95565b60016144fb565b6142899190614af3565b614294906001614a7d565b6063614512565b92505050611314565b60026142b08585614a95565b6142ba9190614af3565b6142c49085614a7d565b4210156142e45760405163e963140160e01b815260040160405180910390fd5b5060329392505050565b600080610b4883111561430357610b48614305565b825b9050600061431e670de0b6b3a76400006209c400614b15565b85111561433f5761433a670de0b6b3a76400006209c400614b15565b614341565b845b9050614358670de0b6b3a764000062013880614b15565b61436764174876e80083614b15565b6143719190614af3565b61033961438364174876e80085614b15565b61438d9190614af3565b613d7c9190614a7d565b60006143a7620151806007614b15565b82116143b557506000919050565b620151806143c560076001614a7d565b6143cf9190614b15565b82116143dd57506001919050565b620151806143ed60076002614a7d565b6143f79190614b15565b821161440557506003919050565b6201518061441560076003614a7d565b61441f9190614b15565b821161442d57506008919050565b6201518061443d60076004614a7d565b6144479190614b15565b821161445557506011919050565b6201518061446560076005614a7d565b61446f9190614b15565b821161447d57506023919050565b6201518061448d60076006614a7d565b6144979190614b15565b82116144a557506048919050565b506063919050565b60008282116144be57506000612a20565b6064670de0b6b3a76400006144d38585614a95565b6144dd8789614b15565b6144e79190614b15565b6144f19190614b15565b613d7c9190614af3565b60008183111561450c575081610dd0565b50919050565b600081831115614523575080610dd0565b5090919050565b6040805160c08101825260008082526020820181905291810182905260608101829052608081018290529060a08201905b905290565b6040518060600160405280600081526020016000815260200161455b61452a565b6040805161014081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290529061012082015290565b604051806080016040528060008152602001600081526020016000815260200161455b614581565b634e487b7160e01b600052601160045260246000fd5b60006001600160581b03808316818516808303821115614633576146336145fb565b01949350505050565b80356001600160a01b038116811461465357600080fd5b919050565b6000806040838503121561466b57600080fd5b6146748361463c565b946020939093013593505050565b634e487b7160e01b600052602160045260246000fd5b60028110610ec657634e487b7160e01b600052602160045260246000fd5b6001600160981b0381511682526001600160801b03602082015116602083015261ffff6040820151166040830152606081015165ffffffffffff8082166060850152806080840151166080850152505060a081015161471481614698565b8060a0840152505050565b60c08101610dd082846146b6565b600060208083528351808285015260005b8181101561475a5785810183015185820160400152820161473e565b8181111561476c576000604083870101525b50601f01601f1916929092016040019392505050565b60006020828403121561479457600080fd5b5035919050565b600080604083850312156147ae57600080fd5b50508035926020909101359150565b6000602082840312156147cf57600080fd5b6113148261463c565b6000806000606084860312156147ed57600080fd5b505081359360208301359350604090920135919050565b60008060006060848603121561481957600080fd5b6148228461463c565b92506148306020850161463c565b9150604084013590509250925092565b602080825282518282018190526000919060409081850190868401855b82811015614897578151805185528681015187860152850151614882868601826146b6565b5061010093909301929085019060010161485d565b5091979650505050505050565b602081016148b183614698565b91905290565b6148c081614698565b9052565b805160ff16825260208101516148e0602084018261ffff169052565b5060408101516148fb60408401826001600160601b03169052565b506060810151614915606084018265ffffffffffff169052565b50608081015161492f608084018265ffffffffffff169052565b5060a081015161494760a084018263ffffffff169052565b5060c081015161495f60c084018263ffffffff169052565b5060e081015161497a60e08401826001600160801b03169052565b50610100818101516001600160401b03169083015261012080820151612e61828501826148b7565b6101408101610dd082846148c4565b600080604083850312156149c457600080fd5b6149cd8361463c565b91506149db6020840161463c565b90509250929050565b602080825282518282018190526000919060409081850190868401855b82811015614897578151805185528681015187860152858101518686015260609081015190614a32818701836148c4565b50506101a0939093019290850190600101614a01565b600181811c90821680614a5c57607f821691505b6020821081141561450c57634e487b7160e01b600052602260045260246000fd5b60008219821115614a9057614a906145fb565b500190565b600082821015614aa757614aa76145fb565b500390565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000600019821415614aec57614aec6145fb565b5060010190565b600082614b1057634e487b7160e01b600052601260045260246000fd5b500490565b6000816000190483118215151615614b2f57614b2f6145fb565b500290565b6001600160981b0382511681526001600160801b03602083015116602082015261ffff6040830151166040820152600065ffffffffffff8060608501511660608401528060808501511660808401525060a0830151614b9281614698565b60a08301525060c00191905056fea26469706673582212202420b775874874a811df5d9b55e7c7b9db72934ae9f753d97fa88e1f0820907c64736f6c634300080a00330000000000000000000000005c23d38719f546e5a9cf725470563ba3f4a3318d

Deployed Bytecode

0x6080604052600436106104075760003560e01c80637d6b325311610213578063baf20eef11610123578063e33a3c94116100ab578063f63ec50e1161007a578063f63ec50e14610c4d578063f80b0cfb14610c68578063faa94d3b14610c7d578063fbf9529d14610cb2578063ffb75cab14610cd257600080fd5b8063e33a3c9414610bc5578063e3af6d0a14610be5578063e3d3227d14610c18578063f2fde38b14610c2d57600080fd5b8063d09a2dda116100f2578063d09a2dda14610b3b578063d819e19814610b50578063d9af94af14610b7d578063dd62ed3e14610b90578063dff96e9a14610bb057600080fd5b8063baf20eef14610abb578063bb88603c14610adb578063c081f4c014610af0578063c50312ad14610b0557600080fd5b806397932d07116101a6578063af4fb76311610175578063af4fb76314610a3a578063af835b8a14610a4f578063b8b9b54914610a7c578063b8fac78914610a91578063b984c94614610aa657600080fd5b806397932d071461099b5780639a5a6cd9146109b0578063a457c2d7146109fa578063a9059cbb14610a1a57600080fd5b806389de4165116101e257806389de41651461092a57806392c1df541461094a578063933281241461095f57806395d89b411461098657600080fd5b80637d6b3253146108b3578063800bb269146108d3578063842e2981146108e8578063880a08361461091557600080fd5b8063313ce56711610319578063635d70f4116102a157806370a082311161027057806370a08231146107ea57806370c9b00214610820578063715018a6146108745780637291fb16146108895780637789281e1461089e57600080fd5b8063635d70f4146107885780636c52876b1461079b5780636e83dc73146107b05780636f609633146107c557600080fd5b8063462a8c2f116102e8578063462a8c2f146107075780634b726c161461073457806350a3a907146107495780635fc768481461075e57806361eb8d011461077357600080fd5b8063313ce5671461069657806339509351146106b25780633a9693e1146106d25780633dda7881146106f257600080fd5b806318160ddd1161039c578063216630b41161036b578063216630b41461060a5780632277d1bd1461061f578063236393851461063457806323b872dd146106495780632d02347a1461066957600080fd5b806318160ddd146105ad5780631851b8bd146105c25780631ae409c0146105e25780631fd979e0146105f757600080fd5b80630cbe28d6116103d85780630cbe28d61461053a5780630fe757c81461055c57806312065fe01461057a578063128bfcae1461058d57600080fd5b8062281d141461048a578062ae5faa146104bb57806306fdde03146104e8578063095ea7b31461050a57600080fd5b36610485576027805434919060009061042a9084906001600160581b0316614611565b92506101000a8154816001600160581b0302191690836001600160581b031602179055503461045860065490565b60405133907fa74c7a39bcbc9995bdd408cb2bef293a21224b85e69d60b9f008c9abbcbff1a390600090a4005b600080fd5b34801561049657600080fd5b50600854600160201b900463ffffffff165b6040519081526020015b60405180910390f35b3480156104c757600080fd5b506104db6104d6366004614658565b610cff565b6040516104b2919061471f565b3480156104f457600080fd5b506104fd610dd6565b6040516104b2919061472d565b34801561051657600080fd5b5061052a610525366004614658565b610e68565b60405190151581526020016104b2565b34801561054657600080fd5b5061055a610555366004614782565b610e80565b005b34801561056857600080fd5b506027546001600160581b03166104a8565b34801561058657600080fd5b50476104a8565b34801561059957600080fd5b5061055a6105a836600461479b565b610ec9565b3480156105b957600080fd5b506002546104a8565b3480156105ce57600080fd5b5061055a6105dd3660046147bd565b610f52565b3480156105ee57600080fd5b506006546104a8565b61055a6106053660046147d8565b610fa3565b34801561061657600080fd5b506011546104a8565b34801561062b57600080fd5b5061055a6110a7565b34801561064057600080fd5b506104a86112de565b34801561065557600080fd5b5061052a610664366004614804565b6112f5565b34801561067557600080fd5b506104a8610684366004614782565b6000908152600d602052604090205490565b3480156106a257600080fd5b50604051601281526020016104b2565b3480156106be57600080fd5b5061052a6106cd366004614658565b61131b565b3480156106de57600080fd5b5061055a6106ed3660046147bd565b61133d565b3480156106fe57600080fd5b5061055a6113ba565b34801561071357600080fd5b506104a8610722366004614782565b60009081526009602052604090205490565b34801561074057600080fd5b506026546104a8565b34801561075557600080fd5b506104a8611485565b34801561076a57600080fd5b506010546104a8565b34801561077f57600080fd5b50601a546104a8565b61055a61079636600461479b565b61149b565b3480156107a757600080fd5b506104a86115c4565b3480156107bc57600080fd5b50600e546104a8565b3480156107d157600080fd5b50600754600160481b90046001600160401b03166104a8565b3480156107f657600080fd5b506104a86108053660046147bd565b6001600160a01b031660009081526020819052604090205490565b34801561082c57600080fd5b5061085f61083b36600461479b565b6000918252600b602090815260408084209284529190529020600181015490549091565b604080519283526020830191909152016104b2565b34801561088057600080fd5b5061055a6115dc565b34801561089557600080fd5b5061055a6115ee565b3480156108aa57600080fd5b506019546104a8565b3480156108bf57600080fd5b5061055a6108ce3660046147bd565b611619565b3480156108df57600080fd5b506104a861166a565b3480156108f457600080fd5b506109086109033660046147bd565b611682565b6040516104b29190614840565b34801561092157600080fd5b506018546104a8565b34801561093657600080fd5b5061085f610945366004614658565b61178a565b34801561095657600080fd5b506013546104a8565b34801561096b57600080fd5b50600854600160401b900460ff166040516104b291906148a4565b34801561099257600080fd5b506104fd6117ce565b3480156109a757600080fd5b506104a86117dd565b3480156109bc57600080fd5b506104a86109cb3660046147bd565b6001600160a01b0316600090815260216020908152604080832082805281842054845290915290206001015490565b348015610a0657600080fd5b5061052a610a15366004614658565b6117e8565b348015610a2657600080fd5b5061052a610a35366004614658565b611873565b348015610a4657600080fd5b506104a8611881565b348015610a5b57600080fd5b506104a8610a6a366004614782565b6000908152600a602052604090205490565b348015610a8857600080fd5b5061055a611893565b348015610a9d57600080fd5b50601c546104a8565b348015610ab257600080fd5b506017546104a8565b348015610ac757600080fd5b5061055a610ad6366004614782565b6118d3565b348015610ae757600080fd5b5061055a6118f2565b348015610afc57600080fd5b50601b546104a8565b348015610b1157600080fd5b506104a8610b203660046147bd565b6001600160a01b031660009081526014602052604090205490565b348015610b4757600080fd5b5061055a61191f565b348015610b5c57600080fd5b50610b70610b6b366004614658565b611927565b6040516104b291906149a2565b348015610b8957600080fd5b50426104a8565b348015610b9c57600080fd5b506104a8610bab3660046149b1565b611a2d565b348015610bbc57600080fd5b506012546104a8565b348015610bd157600080fd5b506104a8610be03660046147bd565b611a58565b348015610bf157600080fd5b507f00000000000000000000000000000000000000000000000000000000671cc8976104a8565b348015610c2457600080fd5b5061055a611ad7565b348015610c3957600080fd5b5061055a610c483660046147bd565b611b7f565b348015610c5957600080fd5b5060085463ffffffff166104a8565b348015610c7457600080fd5b50600f546104a8565b348015610c8957600080fd5b506104a8610c983660046147bd565b6001600160a01b0316600090815260208052604090205490565b348015610cbe57600080fd5b5060075468ffffffffffffffffff166104a8565b348015610cde57600080fd5b50610cf2610ced3660046147bd565b611b90565b6040516104b291906149e4565b610d0761452a565b6001600160a01b0383166000908152601e602090815260408083208584528252808320548352601f825291829020825160c08101845281546001600160981b031681526001808301546001600160801b03811694830194909452600160801b840461ffff1694820194909452600160901b830465ffffffffffff9081166060830152600160c01b840416608082015292909160a0840191600160f01b90910460ff1690811115610db957610db9614682565b6001811115610dca57610dca614682565b90525090505b92915050565b606060038054610de590614a48565b80601f0160208091040260200160405190810160405280929190818152602001828054610e1190614a48565b8015610e5e5780601f10610e3357610100808354040283529160200191610e5e565b820191906000526020600020905b815481529060010190602001808311610e4157829003601f168201915b5050505050905090565b600033610e76818585611ca1565b5060019392505050565b610e88611dc5565b610e906120b6565b610ebc33610eb73384610ea260065490565b600854600190600160401b900460ff16612110565b6122c9565b610ec66001600555565b50565b610ed1611dc5565b610ed96120b6565b81610ee333610805565b1015610f025760405163122cb54d60e21b815260040160405180910390fd5b610f0c3383612388565b610f4433610f3f338585610f2a60075468ffffffffffffffffff1690565b600654600854600160401b900460ff166124ba565b6127b0565b610f4e6001600555565b5050565b610f5a6129d2565b6001600160a01b038116610f8157604051637893a68160e01b815260040160405180910390fd5b602580546001600160a01b0319166001600160a01b0392909216919091179055565b610fab6120b6565b610fb3611dc5565b801580610fc05750606481115b15610fde5760405163819d482b60e01b815260040160405180910390fd5b6103e881610feb33610b20565b610ff59190614a7d565b111561101457604051630c48402960e31b815260040160405180910390fd5b600061101e611485565b1161103c57604051634953318160e11b815260040160405180910390fd5b61108e33848461104a611485565b60085463ffffffff16600854600160201b900463ffffffff16876110898b60016110846007546001600160401b03600160481b9091041690565b6129fd565b612a28565b6110988382612aa4565b6110a26001600555565b505050565b6110af611dc5565b6110b76120b6565b60006110c260195490565b6018546110cf9190614a95565b905060018110156110f3576040516302f86e9360e61b815260040160405180910390fd5b6027546000908190819081906001600160581b03161561112057611115612b83565b929650909450925090505b600061112b60065490565b90506000600161113d601c8985612d63565b600181111561114e5761114e614682565b14801561116c5750600081600181111561116a5761116a614682565b145b611176578061117b565b506001805b50600161118a605a8985612d63565b600181111561119b5761119b614682565b1480156111b9575060008160018111156111b7576111b7614682565b145b6111c357806111c8565b506001805b5060016111d86101718985612d63565b60018111156111e9576111e9614682565b1480156112075750600081600181111561120557611205614682565b145b6112115780611216565b506001805b5060016112266103788985612d63565b600181111561123757611237614682565b1480156112555750600081600181111561125357611253614682565b145b61125f5780611264565b506001805b50600181600181111561127957611279614682565b14156112b9576000600854600160401b900460ff16600181111561129f5761129f614682565b14156112b9576008805460ff60401b1916600160401b1790555b85156112cb576112cb86868686612dee565b505050505050506112dc6001600555565b565b6000600f54600e546112f09190614a95565b905090565b600033611303858285612e67565b61130e858585612edb565b60019150505b9392505050565b600033610e7681858561132e8383611a2d565b6113389190614a7d565b611ca1565b6023546001600160a01b0316336001600160a01b03161461137157604051632409f4d960e11b815260040160405180910390fd5b6001600160a01b03811661139857604051637893a68160e01b815260040160405180910390fd5b602380546001600160a01b0319166001600160a01b0392909216919091179055565b6113c2611dc5565b6113ca6120b6565b60006113d8601c600061307f565b90506113e6605a600061307f565b6113f09082614a7d565b90506113ff610171600061307f565b6114099082614a7d565b9050611418610378600061307f565b6114229082614a7d565b9050806114425760405163046f909560e11b815260040160405180910390fd5b61144d335b826130c3565b604051819033907f106f923f993c2149d49b4255ff723acafa1f2d94393f561d3eda32ae348f724190600090a3506112dc6001600555565b600754600160881b90046001600160601b031690565b6114a36120b6565b6114ab611dc5565b6103e86114b733610b20565b6114c2906001614a7d565b11156114e157604051630c48402960e31b815260040160405180910390fd5b60006114eb611485565b1161150957604051634953318160e11b815260040160405180910390fd5b60008261151560135490565b61151f9190614a7d565b9050600061152c600e5490565b611537906001614a7d565b90506000611589338686611549611485565b60085463ffffffff16600854600160201b900463ffffffff1689896115848e60016110846007546001600160401b03600160481b9091041690565b61315e565b6011546115969190614a7d565b90506115ac828483600e92909255601355601155565b6115b7856001612aa4565b505050610f4e6001600555565b60006115cf601b5490565b6012546112f09190614a7d565b6115e46129d2565b6112dc6000613482565b6115f6611dc5565b6115fe6120b6565b61160f61160a336134a4565b6136e5565b6112dc6001600555565b6116216129d2565b6001600160a01b03811661164857604051637893a68160e01b815260040160405180910390fd5b602480546001600160a01b0319166001600160a01b0392909216919091179055565b6000611675601c5490565b6017546112f09190614a95565b6001600160a01b0381166000908152601d6020526040812054606091816001600160401b038111156116b6576116b6614aac565b6040519080825280602002602001820160405280156116ef57816020015b6116dc614560565b8152602001906001900390816116d45790505b50905060015b82811161178257604080516060810182528281526001600160a01b0387166000908152601e6020908152838220858352815290839020546001600160801b0316908201529081016117468784610cff565b905282611754600184614a95565b8151811061176457611764614ac2565b6020026020010181905250808061177a90614ad8565b9150506116f5565b509392505050565b6001600160a01b03919091166000908152600c6020908152604080832093835292905220546001600160601b03811691600160601b9091046001600160401b031690565b606060048054610de590614a48565b60006112f060105490565b600033816117f68286611a2d565b90508381101561185b5760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084015b60405180910390fd5b6118688286868403611ca1565b506001949350505050565b600033610e76818585612edb565b60006019546018546112f09190614a95565b61189b611dc5565b6118a36120b6565b6000806000806118b1612b83565b93509350935093506118c584848484612dee565b505050506112dc6001600555565b6118db611dc5565b6118e36120b6565b610ebc61160a33836000613718565b6118fa611dc5565b6024546001600160a01b03166000818152602081905260409020546112dc9190612388565b6112dc611dc5565b61192f614581565b6001600160a01b038316600090815260156020908152604080832085845282528083205483526016825291829020825161014081018452815460ff808216835261010080830461ffff1695840195909552630100000082046001600160601b031695830195909552600160781b810465ffffffffffff9081166060840152600160a81b8204166080830152600160d81b900463ffffffff90811660a083015260018084015491821660c0840152600160201b82046001600160801b031660e0840152600160a01b82046001600160401b03169483019490945290939192610120850192600160e01b90920490911690811115610db957610db9614682565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b600080611a6883601c6000613938565b50909150611a7890508183614a7d565b9150611a8783605a6000613938565b50909150611a9790508183614a7d565b9150611aa7836101716000613938565b50909150611ab790508183614a7d565b9150611ac7836103786000613938565b5090915061131490508183614a7d565b6024546001600160a01b0316336001600160a01b031614611b0b57604051632409f4d960e11b815260040160405180910390fd5b6001602754600160581b900460ff166001811115611b2b57611b2b614682565b1415611b4a57604051631e9abe7f60e01b815260040160405180910390fd5b6027805460ff60581b1916600160581b1790556024546112dc906001600160a01b03166b0c1ded63574de0e4660000006122c9565b611b876129d2565b610ec681613482565b6001600160a01b038116600090815260146020526040902054606090806001600160401b03811115611bc457611bc4614aac565b604051908082528060200260200182016040528015611bfd57816020015b611bea6145d3565b815260200190600190039081611be25790505b50915060015b818111611c9a57604080516080810182528281526001600160a01b038616600090815260156020908152838220858352808252848320805483860152928690529052600101549181019190915260608101611c5e8684611927565b905283611c6c600184614a95565b81518110611c7c57611c7c614ac2565b60200260200101819052508080611c9290614ad8565b915050611c03565b5050919050565b6001600160a01b038316611d035760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401611852565b6001600160a01b038216611d645760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401611852565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b600654600062015180611df87f00000000000000000000000000000000000000000000000000000000671cc89742614a95565b611e029190614af3565b611e0d906001614a7d565b905081811115610f4e57600754600854600160481b82046001600160401b03169168ffffffffffffffffff811691600160881b9091046001600160601b03169063ffffffff80821691600160201b9004166000611e6a8888614a95565b905060005b8181101561200357612710611e8661271889614b15565b611e909190614af3565b9650612710611ea161271388614b15565b611eab9190614af3565b9550612710611ebc6126ed87614b15565b611ec69190614af3565b9450612710611ed76126ed86614b15565b611ee19190614af3565b9350670de0b6b3a7640000871115611eff57670de0b6b3a764000096505b6897c9ce4cf6d5c00000861115611f1d576897c9ce4cf6d5c0000095505b670de0b6b3a7640000851015611f5b576a115eec47f6cf7e35000000611f416117dd565b10611f4f5760009450611f5b565b670de0b6b3a764000094505b611f686103e86003614b15565b841015611f7f57611f7c6103e86003614b15565b93505b61015e8811611f9b57611f94616f9b84614a95565b9250611fa0565b600092505b8587611fab8b614ad8565b6040805189815260208101899052908101879052909b508b907fa9283897e800a6f72762253d689720a86b1cc3604e78a9b6ee580413ea30a9539060600160405180910390a480611ffb81614ad8565b915050611e6f565b50600780546001600160601b038616600160881b027fffffff000000000000000000000000ffffffffffffffff0000000000000000006001600160401b038a16600160481b02166001600160e81b03199092169190911768ffffffffffffffffff8816171790556008805463ffffffff848116600160201b0267ffffffffffffffff19909216908616171780825560068990556000919060ff60401b1916600160401b8302179055505050505050505050565b600260055414156121095760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401611852565b6002600555565b6001600160a01b0385166000908152601e60209081526040808320878452909152812054806121525760405163143332f360e11b815260040160405180910390fd5b6000818152601f60209081526040808320815160c08101835281546001600160981b031681526001808301546001600160801b0381169583019590955261ffff600160801b8604169382019390935265ffffffffffff600160901b850481166060830152600160c01b850416608082015292909160a084019160ff600160f01b90910416908111156121e6576121e6614682565b60018111156121f7576121f7614682565b905250905060018160a00151600181111561221457612214614682565b1415612233576040516379776e4760e01b815260040160405180910390fd5b600081602001516001600160801b03169050612260898284600001516001600160981b03168a898b613ab2565b50600186600181111561227557612275614682565b14156122b157601c6000815461228a90614ad8565b909155506000838152601f60205260409020600101805460ff60f01b1916600160f01b1790555b6122bc898484613c64565b9998505050505050505050565b6001600160a01b03821661231f5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401611852565b80600260008282546123319190614a7d565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b6001600160a01b0382166123e85760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401611852565b6001600160a01b0382166000908152602081905260409020548181101561245c5760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401611852565b6001600160a01b0383166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b6001600160a01b0386166000908152601d60205260408120805482919082906124e290614ad8565b918290555090506103e881111561250c576040516372f4ca1560e01b815260040160405180910390fd5b601c86108061251c5750610dac86115b1561253a5760405163689b275f60e01b815260040160405180910390fd5b6000612547888888613d2d565b9050600161255d670de0b6b3a764000083614af3565b101561257c5760405163758012ff60e11b815260040160405180910390fd5b600060176000815461258d90614ad8565b9182905550905060006125a3620151808a614b15565b6125ad9042614a7d565b905060006040518060c001604052808c6001600160981b03168152602001856001600160801b031681526020018b61ffff1681526020014265ffffffffffff1681526020018365ffffffffffff1681526020016000600181111561261357612613614682565b90526001600160a01b038d166000908152601e602090815260408083208984528252808320879055868352601f8252918290208351815472ffffffffffffffffffffffffffffffffffffff19166001600160981b03909116178155908301516001808301805494860151606087015160808801516001600160801b0390951671ffffffffffffffffffffffffffffffffffff1990971696909617600160801b61ffff90921691909102176bffffffffffffffffffffffff60901b1916600160901b65ffffffffffff9687160265ffffffffffff60c01b191617600160c01b95909316949094029190911780845560a0850151949550859492939160ff60f01b1990911690600160f01b90849081111561272e5761272e614682565b02179055509050506127458c858d8b8b6000613ab2565b9550806040516127559190614b34565b6040518091039020838d6001600160a01b03167fecd17a550d3024bd4dcec573e568e747e7843155893d1926213c848215a0d0298d60405161279991815260200190565b60405180910390a450505050509695505050505050565b8060011415610f4e57601c600052600a6020527f964ea767231031507a3f70c59b06c72a2054875e2bc2938da2a55d8f6cb774eb5415610f4e57601c600052600a6020527f964ea767231031507a3f70c59b06c72a2054875e2bc2938da2a55d8f6cb774eb54612821906001614a7d565b6001600160a01b0383166000908152600c60209081526040808320601c84528252822080546001600160601b0319166001600160601b039490941693909317909255605a9052600a90527f7f87218992b43f7ec59f3c8fd242b6759bfedfc613fdc2676bc53b4637f8f35154612898906001614a7d565b6001600160a01b0383166000908152600c60209081526040808320605a84528252822080546001600160601b0319166001600160601b0394909416939093179092556101719052600a90527fb03a258bbb90d8d1843170969b808b3100da20cb067e31b0b691b6f43141902e54612910906001614a7d565b6001600160a01b0383166000908152600c6020908152604080832061017184528252822080546001600160601b0319166001600160601b0394909416939093179092556103789052600a90527fb65719cf4862d40ddcfbadca8d587b82e645261e95d3c4e28fef5a0d6eefb6d654612989906001614a7d565b6001600160a01b0383166000908152600c602090815260408083206103788452909152902080546001600160601b03929092166001600160601b03199092169190911790555050565b6022546001600160a01b031633146112dc576040516341ae395f60e01b815260040160405180910390fd5b6000606483612a0c8685614b15565b612a169190614b15565b612a209190614af3565b949350505050565b601354600e5460115460005b85811015612a8257612a468b85614a7d565b9350612a648c8c8c8c8c8c8a612a5b8b614ad8565b9a508a8d61315e565b612a6e9083614a7d565b915080612a7a81614ad8565b915050612a34565b50612a97828483600e92909255601355601155565b5050505050505050505050565b6000612ac583836110846007546001600160401b03600160481b9091041690565b905080341015612ae85760405163620b518b60e01b815260040160405180910390fd5b6027805460009183918390612b079084906001600160581b0316614611565b92506101000a8154816001600160581b0302191690836001600160581b031602179055508134612b379190614a95565b90508015612b4857612b4833611447565b81612b5260065490565b60405133907fa74c7a39bcbc9995bdd408cb2bef293a21224b85e69d60b9f008c9abbcbff1a390600090a450505050565b6027546000908190819081906001600160581b031680612bb657604051634e63e78360e01b815260040160405180910390fd5b602780546affffffffffffffffffffff19169055604051819033907f55083a582b32208b745a21c8ce4f8d545be8cce1437f34637f08fc9d943eacb090600090a3620f4240612c076119c883614b15565b612c119190614af3565b9450612c1d8582614a95565b905060009350600092506000612c31611485565b1115612c6e57612710612c4661083483614b15565b612c509190614af3565b9350612710612c616103e883614b15565b612c6b9190614af3565b92505b612710612c7d61019083614b15565b612c879190614af3565b915060008284612c978785614a95565b612ca19190614a95565b612cab9190614a95565b90508015612d5b576000612710612cc461096084614b15565b612cce9190614af3565b90506000612710612ce1610d4885614b15565b612ceb9190614af3565b90506000612710612cfe610bb886614b15565b612d089190614af3565b9050612d15601c84613d85565b612d20605a83613d85565b612d2c61017182613d85565b612d576103788284612d3e8789614a95565b612d489190614a95565b612d529190614a95565b613d85565b5050505b505090919293565b6000838152600d6020526040812054821015612d8157506000611314565b612d8a84613dac565b60008481526009602052604090205480612da8576000915050611314565b612db3858286613e17565b506040518190869033907f973833b060ae272a085ff5b17b764e39ddac6a5612bdc9337f586dc7efee604990600090a4506001949350505050565b612df833856130c3565b602354612e0e906001600160a01b0316826130c3565b6000612e18611485565b1115612e6157602454612e34906001600160a01b0316846130c3565b8160266000828254612e469190614a7d565b9091555050602554612e61906001600160a01b0316836130c3565b50505050565b6000612e738484611a2d565b90506000198114612e615781811015612ece5760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401611852565b612e618484848403611ca1565b6001600160a01b038316612f3f5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401611852565b6001600160a01b038216612fa15760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401611852565b6001600160a01b038316600090815260208190526040902054818110156130195760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401611852565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3612e61565b600080808061308f338787613938565b9194509250905060008580156130a7576130a7614682565b14156130b9576130b933878484613e92565b5090949350505050565b6001600160a01b0382166130ea57604051637893a68160e01b815260040160405180910390fd5b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114613137576040519150601f19603f3d011682016040523d82523d6000602084013e61313c565b606091505b50509050806110a25760405163297a66e760e11b815260040160405180910390fd5b600087158061316e575061011888115b1561318c57604051635a2fc35d60e01b815260040160405180910390fd5b8815806131995750606489115b156131b757604051637856200960e11b815260040160405180910390fd5b6131c389898988613f8a565b905060006040518061014001604052808b60ff1681526020018a61ffff168152602001836001600160601b031681526020014265ffffffffffff168152602001620151808b6132129190614b15565b61321c9042614a7d565b65ffffffffffff16815263ffffffff808a166020830152881660408201526000606082018190526001600160401b038616608083015260a0909101526001600160a01b038c16600090815260146020526040812080549293509091829061328290614ad8565b91829055506001600160a01b038d166000908152601560209081526040808320848452825280832089815560019081018b90558984526016835292819020865181549388015192880151606089015160808a015160a08b015160ff90941662ffffff199097169690961761010061ffff90961686021774ffffffffffffffffffffffffffffffffffff000000191663010000006001600160601b039093169290920265ffffffffffff60781b191691909117600160781b65ffffffffffff928316021769ffffffffffffffffffff60a81b1916600160a81b919095160263ffffffff60d81b191693909317600160d81b63ffffffff9485160217815560c0870151818501805460e08a0151948a0151929095166001600160a01b031990951694909417600160201b6001600160801b03909416939093029290921767ffffffffffffffff60a01b198116600160a01b6001600160401b039094169390930292831784556101208801519596508795919491939268ffffffffffffffffff60a01b1990911660ff60e01b199091161790600160e01b90849081111561342857613428614682565b021790555090505085858d6001600160a01b03167f2109b8587b0ddbd9adf8ec24ce76bef548f2aee7aac34bc6aa0bb51b7cba9d678560405161346b91906149a2565b60405180910390a450509998505050505050505050565b602280546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0381166000908152601460205260408120548180806134c8614581565b60015b8581116136a9576001600160a01b03881660009081526015602090815260408083208484528252808320548084526016835292819020815161014081018352815460ff808216835261010080830461ffff1696840196909652630100000082046001600160601b031694830194909452600160781b810465ffffffffffff9081166060840152600160a81b8204166080830152600160d81b900463ffffffff90811660a083015260018084015491821660c0840152600160201b82046001600160801b031660e0840152600160a01b82046001600160401b031695830195909552949850939092610120850192600160e01b909204909116908111156135d3576135d3614682565b60018111156135e4576135e4614682565b90525091506000826101200151600181111561360257613602614682565b14801561361b5750816080015165ffffffffffff164210155b15613689576001600160a01b0388166000908152601560209081526040808320848452909152812060010154613656918a9187918690614035565b6136609088614a7d565b965081604001516001600160601b03168361367b9190614a7d565b925061368685614ad8565b94505b8460641415613697576136a9565b806136a181614ad8565b9150506134cb565b5081601160008282546136bc9190614a95565b9250508190555081601060008282546136d59190614a7d565b9091555095979650505050505050565b6136ef33826122c9565b602354610ec6906001600160a01b031661271061370e846101f4614b15565b610eb79190614af3565b6001600160a01b0383166000908152601560209081526040808320858452909152812080546001909101548161376157604051634e32f3a360e11b815260040160405180910390fd5b6000828152601660209081526040808320815161014081018352815460ff808216835261010080830461ffff1696840196909652630100000082046001600160601b031694830194909452600160781b810465ffffffffffff9081166060840152600160a81b8204166080830152600160d81b900463ffffffff90811660a083015260018084015491821660c0840152600160201b82046001600160801b031660e0840152600160a01b82046001600160401b03169583019590955290939192610120850192600160e01b909204169081111561384057613840614682565b600181111561385157613851614682565b90525090506001816101200151600181111561386f5761386f614682565b141561388e576040516399adb6e160e01b815260040160405180910390fd5b42816080015165ffffffffffff161180156138b7575060008580156138b5576138b5614682565b145b156138d5576040516311023b4960e31b815260040160405180910390fd5b80604001516001600160601b0316601160008282546138f49190614a95565b9250508190555080604001516001600160601b03166010600082825461391a9190614a7d565b9091555061392d90508784848489614035565b979650505050505050565b600080600080613954866000908152600a602052604090205490565b9050600085801561396757613967614682565b1415613aa857613977878761178a565b9093509150600061399c886001600160a01b0316600090815260208052604090205490565b9050835b828111613aa5576000888152600b602090815260408083208484529091528120600181015490549091865b858111613a44576001600160a01b038d1660009081526021602090815260408083208484529091529020548310613a2a576001600160a01b038d1660009081526021602090815260408083208484529091529020600101549150613a2f565b613a44565b96508680613a3c81614ad8565b9150506139cb565b508215801590613a5357508015155b15613a8257670de0b6b3a7640000613a6b8483614b15565b613a759190614af3565b613a7f908a614a7d565b98505b613a8d846001614a7d565b97505050508080613a9d90614ad8565b9150506139a0565b50505b5093509350939050565b6001600160a01b038616600090815260208080526040808320546021835281842081855290925282206001015482846001811115613af257613af2614682565b1415613b7c5781613b0257600192505b613b0c8882614a7d565b6001600160a01b038a16600090815260216020526040812090613b2e85614ad8565b9450848152602001908152602001600020600101819055508760186000828254613b589190614a7d565b9250508190555086601a6000828254613b719190614a7d565b90915550613bf19050565b613b868882614a95565b6001600160a01b038a16600090815260216020526040812090613ba885614ad8565b9450848152602001908152602001600020600101819055508760196000828254613bd29190614a7d565b9250508190555086601a6000828254613beb9190614a95565b90915550505b6000856001811115613c0557613c05614682565b14613c1a57613c15866001614a7d565b613c1c565b855b6001600160a01b03909916600081815260216020908152604080832086845282528083206001600160801b039d909d16909c5591815290805298909820559695505050505050565b60008082600001516001600160981b031690506000613c9c846060015165ffffffffffff16856080015165ffffffffffff1642614228565b905060006064613cac8385614b15565b613cb69190614af3565b9050613cc28184614a95565b935080601b6000828254613cd69190614a7d565b90915550506040805185815260208101839052839188916001600160a01b038b16917f971d9ff3287b3ba75194105e7281e55c93b0a89cad9915664bb3fd9211f8d5f1910160405180910390a45050509392505050565b60008364174876e800613d4082866142ee565b613d4a9083614b15565b613d549190614af3565b613d5e9082614a7d565b9050613d72670de0b6b3a764000084614af3565b613d7c9082614af3565b95945050505050565b60008281526009602052604081208054839290613da3908490614a7d565b90915550505050565b6000818152600d60205260409020546006548181106110a25782613dd08383614a95565b613dda9190614af3565b613de5906001614a7d565b613def9084614b15565b6000848152600d602052604081208054909190613e0d908490614a7d565b9091555050505050565b6000838152600960209081526040808320839055600a909152812080548290613e3f90614ad8565b9182905550905081613e59670de0b6b3a764000085614b15565b613e639190614af3565b6000948552600b6020908152604080872084885290915290942060018101949094556006549093555090919050565b6001600160a01b0384166000908152600c602090815260408083208684529091529020546001600160601b03168214613f02576001600160a01b0384166000908152600c60209081526040808320868452909152902080546001600160601b0319166001600160601b0384161790555b6001600160a01b0384166000908152600c60209081526040808320868452909152902054600160601b90046001600160401b03168114612e61576001600160a01b0384166000908152600c60209081526040808320868452909152902080546001600160401b038316600160601b0267ffffffffffffffff60601b1990911617905550505050565b60008084613f988786614b15565b613fa29190614b15565b905084600114613fe657612710613fba600187614a95565b613fc5600b84614b15565b613fcf9190614b15565b613fd99190614af3565b613fe39082614a95565b90505b905080821561402057620f42406064613fff8584614b15565b6140099190614af3565b6140139190614af3565b61401d9083614a7d565b91505b61402b606483614af3565b9695505050505050565b60008082801561404757614047614682565b1415614070576000858152601660205260409020600101805460ff60e01b1916600160e01b1790555b6000806000856080015165ffffffffffff164211156140ac576140a9866080015165ffffffffffff16426140a49190614a95565b614397565b91505b60008580156140bd576140bd614682565b14156140e5576140e28660a0015163ffffffff16876000015160ff16896013546144ad565b90505b6140f2620f424082614af3565b86604001516001600160601b031661410a9190614a7d565b935060646141188386614b15565b6141229190614af3565b925061412e8385614a95565b9350600085801561414157614141614682565b141561415b57600f6000815461415690614ad8565b909155505b82156141795782601260008282546141739190614a7d565b90915550505b600085801561418a5761418a614682565b14156141ce576000888152601660205260409020600101805473ffffffffffffffffffffffffffffffff000000001916600160201b6001600160801b038716021790555b81888a6001600160a01b03167fbd866a3fbf35e201f790e87581b1afbb3165e879df5d35313a4875a70b9f3b368787604051614214929190918252602082015260400190565b60405180910390a450505095945050505050565b6000828211156142a457600061423e8484614a95565b90506000614250620151806007614b15565b905080821161426457600092505050611314565b61429b6201518061427f6142788486614a95565b60016144fb565b6142899190614af3565b614294906001614a7d565b6063614512565b92505050611314565b60026142b08585614a95565b6142ba9190614af3565b6142c49085614a7d565b4210156142e45760405163e963140160e01b815260040160405180910390fd5b5060329392505050565b600080610b4883111561430357610b48614305565b825b9050600061431e670de0b6b3a76400006209c400614b15565b85111561433f5761433a670de0b6b3a76400006209c400614b15565b614341565b845b9050614358670de0b6b3a764000062013880614b15565b61436764174876e80083614b15565b6143719190614af3565b61033961438364174876e80085614b15565b61438d9190614af3565b613d7c9190614a7d565b60006143a7620151806007614b15565b82116143b557506000919050565b620151806143c560076001614a7d565b6143cf9190614b15565b82116143dd57506001919050565b620151806143ed60076002614a7d565b6143f79190614b15565b821161440557506003919050565b6201518061441560076003614a7d565b61441f9190614b15565b821161442d57506008919050565b6201518061443d60076004614a7d565b6144479190614b15565b821161445557506011919050565b6201518061446560076005614a7d565b61446f9190614b15565b821161447d57506023919050565b6201518061448d60076006614a7d565b6144979190614b15565b82116144a557506048919050565b506063919050565b60008282116144be57506000612a20565b6064670de0b6b3a76400006144d38585614a95565b6144dd8789614b15565b6144e79190614b15565b6144f19190614b15565b613d7c9190614af3565b60008183111561450c575081610dd0565b50919050565b600081831115614523575080610dd0565b5090919050565b6040805160c08101825260008082526020820181905291810182905260608101829052608081018290529060a08201905b905290565b6040518060600160405280600081526020016000815260200161455b61452a565b6040805161014081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290529061012082015290565b604051806080016040528060008152602001600081526020016000815260200161455b614581565b634e487b7160e01b600052601160045260246000fd5b60006001600160581b03808316818516808303821115614633576146336145fb565b01949350505050565b80356001600160a01b038116811461465357600080fd5b919050565b6000806040838503121561466b57600080fd5b6146748361463c565b946020939093013593505050565b634e487b7160e01b600052602160045260246000fd5b60028110610ec657634e487b7160e01b600052602160045260246000fd5b6001600160981b0381511682526001600160801b03602082015116602083015261ffff6040820151166040830152606081015165ffffffffffff8082166060850152806080840151166080850152505060a081015161471481614698565b8060a0840152505050565b60c08101610dd082846146b6565b600060208083528351808285015260005b8181101561475a5785810183015185820160400152820161473e565b8181111561476c576000604083870101525b50601f01601f1916929092016040019392505050565b60006020828403121561479457600080fd5b5035919050565b600080604083850312156147ae57600080fd5b50508035926020909101359150565b6000602082840312156147cf57600080fd5b6113148261463c565b6000806000606084860312156147ed57600080fd5b505081359360208301359350604090920135919050565b60008060006060848603121561481957600080fd5b6148228461463c565b92506148306020850161463c565b9150604084013590509250925092565b602080825282518282018190526000919060409081850190868401855b82811015614897578151805185528681015187860152850151614882868601826146b6565b5061010093909301929085019060010161485d565b5091979650505050505050565b602081016148b183614698565b91905290565b6148c081614698565b9052565b805160ff16825260208101516148e0602084018261ffff169052565b5060408101516148fb60408401826001600160601b03169052565b506060810151614915606084018265ffffffffffff169052565b50608081015161492f608084018265ffffffffffff169052565b5060a081015161494760a084018263ffffffff169052565b5060c081015161495f60c084018263ffffffff169052565b5060e081015161497a60e08401826001600160801b03169052565b50610100818101516001600160401b03169083015261012080820151612e61828501826148b7565b6101408101610dd082846148c4565b600080604083850312156149c457600080fd5b6149cd8361463c565b91506149db6020840161463c565b90509250929050565b602080825282518282018190526000919060409081850190868401855b82811015614897578151805185528681015187860152858101518686015260609081015190614a32818701836148c4565b50506101a0939093019290850190600101614a01565b600181811c90821680614a5c57607f821691505b6020821081141561450c57634e487b7160e01b600052602260045260246000fd5b60008219821115614a9057614a906145fb565b500190565b600082821015614aa757614aa76145fb565b500390565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000600019821415614aec57614aec6145fb565b5060010190565b600082614b1057634e487b7160e01b600052601260045260246000fd5b500490565b6000816000190483118215151615614b2f57614b2f6145fb565b500290565b6001600160981b0382511681526001600160801b03602083015116602082015261ffff6040830151166040820152600065ffffffffffff8060608501511660608401528060808501511660808401525060a0830151614b9281614698565b60a08301525060c00191905056fea26469706673582212202420b775874874a811df5d9b55e7c7b9db72934ae9f753d97fa88e1f0820907c64736f6c634300080a0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000005c23d38719f546e5a9cf725470563ba3f4a3318d

-----Decoded View---------------
Arg [0] : genesisAddress (address): 0x5c23d38719f546e5a9cf725470563ba3F4A3318d

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000005c23d38719f546e5a9cf725470563ba3f4a3318d


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

OVERVIEW

Legacy is 100% community-owned currency of the resistance with finite supply. The reason Legacy has been created is to allow people to preserve the monetary energy that they created and store the value of their wealth in a digital prosperity vault on a blockchain.

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ 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.