ETH Price: $1,789.37 (+13.39%)
 

Overview

ETH Balance

3.374324152150125501 ETH

Eth Value

$6,037.92 (@ $1,789.37/ETH)

Token Holdings

More Info

Private Name Tags

TokenTracker

HELIOS (HLX) (@$0.00)

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Claim User Avail...223292522025-04-23 4:14:4731 secs ago1745381687IN
Helios: HLX Token
0 ETH0.000114370.6
Claim User Avail...223290932025-04-23 3:42:3532 mins ago1745379755IN
Helios: HLX Token
0 ETH0.000121450.62768259
Claim User Avail...223290852025-04-23 3:40:5934 mins ago1745379659IN
Helios: HLX Token
0 ETH0.000212111.11272727
End Stake223290042025-04-23 3:24:4750 mins ago1745378687IN
Helios: HLX Token
0 ETH0.000091980.63630996
End Stake223287962025-04-23 2:42:471 hr ago1745376167IN
Helios: HLX Token
0 ETH0.000139251.23212391
Claim User Avail...223287942025-04-23 2:42:231 hr ago1745376143IN
Helios: HLX Token
0 ETH0.000067591.22475261
Claim User Avail...223287932025-04-23 2:42:111 hr ago1745376131IN
Helios: HLX Token
0 ETH0.000216821.1
Claim User Avail...223287322025-04-23 2:29:591 hr ago1745375399IN
Helios: HLX Token
0 ETH0.000319321.5
Claim User Avail...223286032025-04-23 2:03:472 hrs ago1745373827IN
Helios: HLX Token
0 ETH0.000210981.33298024
Claim Mint223285502025-04-23 1:52:232 hrs ago1745373143IN
Helios: HLX Token
0 ETH0.000168221.10825045
End Stake223284392025-04-23 1:29:592 hrs ago1745371799IN
Helios: HLX Token
0 ETH0.00024111.46604156
Claim User Avail...223284332025-04-23 1:28:472 hrs ago1745371727IN
Helios: HLX Token
0 ETH0.00014891
Claim User Avail...223284152025-04-23 1:25:112 hrs ago1745371511IN
Helios: HLX Token
0 ETH0.000178481.35829444
Claim User Avail...223283092025-04-23 1:03:593 hrs ago1745370239IN
Helios: HLX Token
0 ETH0.000367172.06036159
Claim User Avail...223282192025-04-23 0:45:593 hrs ago1745369159IN
Helios: HLX Token
0 ETH0.000218431.1488358
Claim User Avail...223282152025-04-23 0:45:113 hrs ago1745369111IN
Helios: HLX Token
0 ETH0.000189761.05418356
Claim Mint223281422025-04-23 0:30:353 hrs ago1745368235IN
Helios: HLX Token
0 ETH0.000193111.2722924
Claim User Avail...223280442025-04-23 0:10:594 hrs ago1745367059IN
Helios: HLX Token
0 ETH0.000248931.30588501
Claim User Avail...223280172025-04-23 0:05:354 hrs ago1745366735IN
Helios: HLX Token
0 ETH0.000149370.87515266
Claim User Avail...223276792025-04-22 22:57:355 hrs ago1745362655IN
Helios: HLX Token
0 ETH0.000531832.32259907
Start Stake223276532025-04-22 22:52:235 hrs ago1745362343IN
Helios: HLX Token
0 ETH0.000541852.57407609
Claim User Avail...223276482025-04-22 22:51:235 hrs ago1745362283IN
Helios: HLX Token
0 ETH0.00030521.73311351
Claim User Avail...223276102025-04-22 22:43:475 hrs ago1745361827IN
Helios: HLX Token
0 ETH0.000597942.87859064
Claim User Avail...223272012025-04-22 21:21:236 hrs ago1745356883IN
Helios: HLX Token
0 ETH0.000223591.07643007
Claim User Avail...223271652025-04-22 21:14:117 hrs ago1745356451IN
Helios: HLX Token
0 ETH0.000489312
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
Transfer223292522025-04-23 4:14:4731 secs ago1745381687
Helios: HLX Token
0.00229485 ETH
Transfer223290932025-04-23 3:42:3532 mins ago1745379755
Helios: HLX Token
0.00664198 ETH
Transfer223290852025-04-23 3:40:5934 mins ago1745379659
Helios: HLX Token
0.00103846 ETH
Transfer223287932025-04-23 2:42:111 hr ago1745376131
Helios: HLX Token
0.00257918 ETH
Transfer223287322025-04-23 2:29:591 hr ago1745375399
Helios: HLX Token
0.00779284 ETH
Transfer223286032025-04-23 2:03:472 hrs ago1745373827
Helios: HLX Token
0.00224482 ETH
Transfer223284332025-04-23 1:28:472 hrs ago1745371727
Helios: HLX Token
0.00050285 ETH
Transfer223284152025-04-23 1:25:112 hrs ago1745371511
Helios: HLX Token
0.01598638 ETH
Transfer223283092025-04-23 1:03:593 hrs ago1745370239
Helios: HLX Token
0.0073727 ETH
Transfer223282192025-04-23 0:45:593 hrs ago1745369159
Helios: HLX Token
0.15341256 ETH
Transfer223282152025-04-23 0:45:113 hrs ago1745369111
Helios: HLX Token
0.09049247 ETH
Transfer223280442025-04-23 0:10:594 hrs ago1745367059
Helios: HLX Token
0.00474974 ETH
Transfer223280172025-04-23 0:05:354 hrs ago1745366735
Helios: HLX Token
0.00093461 ETH
Transfer223276792025-04-22 22:57:355 hrs ago1745362655
Helios: HLX Token
0.05481722 ETH
Transfer223276482025-04-22 22:51:235 hrs ago1745362283
Helios: HLX Token
0.01435194 ETH
Transfer223276102025-04-22 22:43:475 hrs ago1745361827
Helios: HLX Token
0.00480062 ETH
Transfer223272012025-04-22 21:21:236 hrs ago1745356883
Helios: HLX Token
0.06573436 ETH
Transfer223271652025-04-22 21:14:117 hrs ago1745356451
Helios: HLX Token
0.17237807 ETH
Transfer223270872025-04-22 20:58:237 hrs ago1745355503
Helios: HLX Token
0.01183229 ETH
Transfer223270702025-04-22 20:54:477 hrs ago1745355287
Helios: HLX Token
0.01233038 ETH
Transfer223269692025-04-22 20:34:357 hrs ago1745354075
Helios: HLX Token
0.0992133 ETH
Transfer223269472025-04-22 20:30:117 hrs ago1745353811
Helios: HLX Token
0.01118988 ETH
Transfer223269032025-04-22 20:21:237 hrs ago1745353283
Helios: HLX Token
0.02455217 ETH
Transfer223264312025-04-22 18:46:239 hrs ago1745347583
Helios: HLX Token
0.00206288 ETH
Transfer223264252025-04-22 18:45:119 hrs ago1745347511
Helios: HLX Token
0.00441456 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
HELIOS

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
Yes with 0 runs

Other Settings:
default evmVersion
File 1 of 20 : Helios.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/interfaces/IERC165.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import "../interfaces/IHlxOnBurn.sol";
import "../interfaces/ITITANX.sol";
import "../interfaces/ITitanOnBurn.sol";
import "../interfaces/IBuynBurn.sol";

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

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

//custom errors
error Helios_InvalidAmount();
error Helios_InsufficientBalance();
error Helios_NotSupportedContract();
error Helios_InsufficientProtocolFees();
error Helios_FailedToSendAmount();
error Helios_NotAllowed();
error Helios_NoCycleRewardToClaim();
error Helios_NoSharesExist();
error Helios_EmptyUndistributeFees();
error Helios_InvalidBurnRewardPercent();
error Helios_MaxedWalletMints();
error Helios_LPTokensHasMinted();
error Helios_InvalidAddress();
error Helios_InsufficientBurnAllowance();
error Helios_OnlyBuyAndBurn();
error Helios_OnlyWhiteListedProjects();

/** @title Helios */
contract HELIOS is
    ERC20,
    ReentrancyGuard,
    GlobalInfo,
    MintInfo,
    StakeInfo,
    BurnInfo,
    OwnerInfo,
    IERC165,
    ITitanOnBurn
{
    /** Storage Variables*/
    /** @dev stores genesis wallet address */
    address private s_genesisAddress;

    /** @dev stores Investment address */
    address private s_investmentAddress;

    /** @dev stores buy and burn contract address */
    address private s_buyAndBurnAddress;

    /** @dev stores treasury contract address */
    address private s_treasuryAddress;

    /** @dev stores TITANX contract address */
    address private s_titanxAddress;

    /** @dev tracks collected protocol fees until it is distributed */
    uint256 private s_undistributedTitanX;

    /** @dev tracks collected protocol fees until it is distributed */
    uint256 private s_undistributedETH;

    /** @dev stores total Titanx burned by Users  */
    uint256 private s_totalTitanBurned;

    // /** @dev tracks burn reward from distributeTitanX() until payout is triggered */
    // uint88 private s_cycleBurnReward;

    /** @dev tracks if initial LP tokens has minted or not */
    InitialLPMinted private s_initialLPMinted;

    // /** @dev trigger to turn on burn pool reward */
    // BurnPoolEnabled private s_burnPoolEnabled;

    /** @dev tracks user + project burn mints allowance */
    mapping(address => mapping(address => uint256))
        private s_allowanceBurnMints;

    /** @dev tracks projects whiteListed to stake on hlx */
    mapping(address => bool) private s_whiteList;

    /** @dev tracks user + project burn stakes allowance */
    mapping(address => mapping(address => uint256))
        private s_allowanceBurnStakes;

    struct MintParams {
        uint256 mintPower;
        uint256 numOfDays;
        uint256 titanToBurn;
        uint256 gMintPower;
        uint256 currentHRank;
        uint256 mintCost;
    }

    event ProtocolFeeRecevied(
        address indexed user,
        uint256 indexed day,
        uint256 indexed amount
    );
    event TitanXDistributed(address indexed caller, uint256 indexed amount);
    event CyclePayoutTriggered(
        address indexed caller,
        uint256 indexed cycleNo,
        uint256 indexed reward
        // uint256 burnReward
    );
    event RewardClaimed(
        address indexed user,
        uint256 indexed reward,
        uint256 indexed ethReward
    );
    event ApproveBurnStakes(
        address indexed user,
        address indexed project,
        uint256 indexed amount
    );
    event ApproveBurnMints(
        address indexed user,
        address indexed project,
        uint256 indexed amount
    );

    constructor(
        address genesisAddress,
        address buyAndBurnAddress,
        address titanxAddress,
        address treasuryAddress,
        address investmentAddress
    ) ERC20("HELIOS", "HLX") {
        if (genesisAddress == address(0)) revert Helios_InvalidAddress();
        if (buyAndBurnAddress == address(0)) revert Helios_InvalidAddress();
        if (titanxAddress == address(0)) revert Helios_InvalidAddress();
        if (treasuryAddress == address(0)) revert Helios_InvalidAddress();
        s_genesisAddress = genesisAddress;
        s_investmentAddress = investmentAddress;
        s_buyAndBurnAddress = buyAndBurnAddress;
        s_titanxAddress = titanxAddress;
        s_treasuryAddress = treasuryAddress;
    }

    modifier onlyBuyAndBurn() {
        if (_msgSender() != s_buyAndBurnAddress) revert Helios_OnlyBuyAndBurn();
        _;
    }

    function supportsInterface(
        bytes4 interfaceId
    ) external pure override returns (bool) {
        return
            interfaceId == INTERFACE_ID_ERC165 ||
            interfaceId == INTERFACE_ID_ITITANONBURN;
    }

    function onBurn(address, uint256 amount) external override {
        require(msg.sender == s_titanxAddress, "Only TitanX");
        s_totalTitanBurned += amount;
    }

    /**** Mint Functions *****/
    /** @notice create a new mint
     * @param mintPower 1 - 100,000
     * @param numOfDays mint length of 1 - 250
     */
    function startMint(
        uint256 mintPower,
        uint256 numOfDays,
        uint256 titanToBurn
    ) external payable nonReentrant dailyUpdate {
        if (getUserLatestMintId(_msgSender()) + 1 > MAX_MINT_PER_WALLET)
            revert Helios_MaxedWalletMints();

        if (titanToBurn > 0) _burnTitanX(titanToBurn);

        MintParams memory params = MintParams({
            mintPower: mintPower,
            numOfDays: numOfDays,
            titanToBurn: titanToBurn,
            gMintPower: getGlobalMintPower() + mintPower,
            currentHRank: getGlobalHRank() + 1,
            mintCost: getMintCost(mintPower, getCurrentMintCost())
        });

        uint256 gMinting = getTotalMinting() +
            _startMint(
                _msgSender(),
                params.mintPower,
                params.numOfDays,
                getCurrentMintableHlx(),
                getCurrentMintPowerBonus(),
                getCurrentEAABonus(),
                getUserBurnAmplifierBonus(_msgSender()),
                params.gMintPower,
                params.currentHRank,
                params.mintCost,
                params.titanToBurn
            );
        _updateMintStats(params.currentHRank, params.gMintPower, gMinting);
        _protocolFees(mintPower);
    }

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

    /**** Stake Functions *****/
    /** @notice start a new stake
     * @param amount Helios amount
     * @param numOfDays stake length
     * @param titanToBurn amount of titanX tokens to burn to get reward
     */
    function startStake(
        uint256 amount,
        uint256 numOfDays,
        uint256 titanToBurn
    ) external dailyUpdate nonReentrant {
        if (balanceOf(_msgSender()) < amount)
            revert Helios_InsufficientBalance();

        if (msg.sender != tx.origin) {
            // check if it's whitelisted
            require(s_whiteList[msg.sender], "Contract not whitelisted.");
        }

        if (titanToBurn > 0) _burnTitanX(titanToBurn);

        _burn(_msgSender(), amount);
        _initFirstSharesCycleIndex(
            _msgSender(),
            _startStake(
                _msgSender(),
                amount,
                numOfDays,
                getCurrentShareRate(),
                getCurrentContractDay(),
                getGlobalPayoutTriggered(),
                titanToBurn,
                titanToBurn > 0
                    ? IBuynBurn(s_buyAndBurnAddress).getCurrentTitanPrice()
                    : 0
            )
        );
    }

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

    /** @notice end a stake for others
     * @param user wallet address
     * @param id stake id
     */
    function endStakeForOthers(
        address user,
        uint256 id
    ) external dailyUpdate nonReentrant {
        _mint(
            user,
            _endStake(
                user,
                id,
                getCurrentContractDay(),
                StakeAction.END,
                StakeAction.END_OTHER,
                getGlobalPayoutTriggered()
            )
        );
    }

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

    /** @notice trigger cylce payouts for day 22, 69, 420
     * As long as the cycle has met its maturiy day (eg. Cycle22 is day 22), payout can be triggered in any day onwards
     */
    function triggerPayouts() external dailyUpdate nonReentrant {
        uint256 globalActiveShares = getGlobalShares() -
            getGlobalExpiredShares();
        if (globalActiveShares < 1) revert Helios_NoSharesExist();

        uint256 incentiveFee;
        uint256 buyAndBurnFunds;
        uint256 genesisWallet;
        uint256 treasuryReward;

        if (s_undistributedTitanX != 0)
            (
                incentiveFee,
                buyAndBurnFunds,
                treasuryReward,
                genesisWallet
            ) = _distributeTitanX();

        uint256 currentContractDay = getCurrentContractDay();
        PayoutTriggered isTriggered = PayoutTriggered.NO;

        _triggerCyclePayout(DAY22, globalActiveShares, currentContractDay) ==
            PayoutTriggered.YES &&
            isTriggered == PayoutTriggered.NO
            ? isTriggered = PayoutTriggered.YES
            : isTriggered;
        _triggerCyclePayout(DAY69, globalActiveShares, currentContractDay) ==
            PayoutTriggered.YES &&
            isTriggered == PayoutTriggered.NO
            ? isTriggered = PayoutTriggered.YES
            : isTriggered;
        _triggerCyclePayout(DAY420, 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,
                treasuryReward,
                genesisWallet
            );
    }

    /** @notice claim all user available TitanX/ETH payouts in one call */
    function claimUserAvailablePayouts() external dailyUpdate nonReentrant {
        uint256 totalTitanXReward = 0;
        uint256 totalEthReward = 0;

        (uint256 reward, uint256 ethReward) = _claimCyclePayout(DAY22);
        totalTitanXReward += reward;
        totalEthReward += ethReward;

        (reward, ethReward) = _claimCyclePayout(DAY69);
        totalTitanXReward += reward;
        totalEthReward += ethReward;

        (reward, ethReward) = _claimCyclePayout(DAY420);
        totalTitanXReward += reward;
        totalEthReward += ethReward;

        if (totalTitanXReward == 0 && totalEthReward == 0)
            revert Helios_NoCycleRewardToClaim();

        if (totalTitanXReward > 0) {
            _sendTitanX(_msgSender(), totalTitanXReward);
        }

        if (totalEthReward > 0) {
            _sendViaCall(payable(_msgSender()), totalEthReward);
        }
        emit RewardClaimed(_msgSender(), totalTitanXReward, totalEthReward);
    }

    /** @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 Helios_InvalidAddress();
        s_buyAndBurnAddress = contractAddress;
    }

    /** @notice adds address to whitelist
     * Only owner can call this function
     * @param contractAddress project contract address
     * @param permit bool  True to allow
     */
    function whiteList(
        address contractAddress,
        bool permit
    ) external onlyOwner {
        if (contractAddress == address(0)) revert Helios_InvalidAddress();
        s_whiteList[contractAddress] = permit;
    }

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

    /** @notice Set TitanX Contract Address - able to change to new contract that supports UniswapV4+
     * Only owner can call this function
     * @param contractAddress TitanX contract address
     */
    function setTitanXContractAddress(
        address contractAddress
    ) external onlyOwner {
        if (contractAddress == address(0)) revert Helios_InvalidAddress();
        s_titanxAddress = 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 Helios_NotAllowed();
        if (newAddress == address(0)) revert Helios_InvalidAddress();
        s_genesisAddress = newAddress;
    }

    /** @notice Set to new Investment Address.
     * @param newAddress new Investment address
     */
    function setNewInvestmentAddress(address newAddress) external {
        if (_msgSender() != s_investmentAddress) revert Helios_NotAllowed();
        if (newAddress == address(0)) revert Helios_InvalidAddress();
        s_investmentAddress = 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 Helios_NotAllowed();
        if (s_initialLPMinted == InitialLPMinted.YES)
            revert Helios_LPTokensHasMinted();
        s_initialLPMinted = InitialLPMinted.YES;
        _mint(s_buyAndBurnAddress, INITAL_LP_TOKENS);
    }

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

    //private functions
    /** @dev mint reward to user and 1% to genesis wallet
     * @param reward helios amount
     */
    function _mintReward(uint256 reward) private {
        _mint(_msgSender(), reward);
        _mint(s_investmentAddress, (reward * 100) / PERCENT_BPS);
    }

    /** @dev burns given amount of titanX with giving reward to caller and genesis Wallet
     * @param titanAmount amount titanX to burn
     */
    function _burnTitanX(uint256 titanAmount) private {
        ITITANX(TITANX).burnTokensToPayAddress(
            _msgSender(),
            titanAmount,
            BURN_REWARD_PERCENT_EACH,
            BURN_REWARD_PERCENT_EACH,
            s_genesisAddress
        );
    }

    /** @dev send TitanX to respective parties
     * @param incentiveFee fees for caller to run distributeTitanX()
     * @param buyAndBurnFunds funds for buy and burn
     * @param genesisWalletFunds funds for genesis wallet
     */
    function _sendFunds(
        uint256 incentiveFee,
        uint256 buyAndBurnFunds,
        uint256 treasuryReward,
        uint256 genesisWalletFunds
    ) private {
        _sendTitanX(_msgSender(), incentiveFee);
        _sendTitanX(s_genesisAddress, genesisWalletFunds);
        _sendTitanX(s_buyAndBurnAddress, buyAndBurnFunds);
        _sendTitanX(s_treasuryAddress, treasuryReward);
    }

    /** @dev calculation to distribute collected protocol fees into different pools/parties */
    function _distributeTitanX()
        private
        returns (
            uint256 incentiveFee,
            uint256 buyAndBurnFunds,
            uint256 treasuryReward,
            uint256 genesisWallet
        )
    {
        uint256 accumulatedFees = s_undistributedTitanX;
        if (accumulatedFees == 0) revert Helios_EmptyUndistributeFees();
        s_undistributedTitanX = 0;
        emit TitanXDistributed(_msgSender(), accumulatedFees);

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

        buyAndBurnFunds =
            (accumulatedFees * getBuynBurnPercentage()) /
            PERCENT_BPS;
        treasuryReward =
            (accumulatedFees * getTreasuryPercentage()) /
            PERCENT_BPS;
        genesisWallet = (accumulatedFees * PERCENT_TO_GENESIS) / PERCENT_BPS;
        uint256 cycleRewardPool = accumulatedFees -
            buyAndBurnFunds -
            treasuryReward -
            genesisWallet;

        //cycle payout
        if (cycleRewardPool != 0) {
            uint256 cycle22Reward = (cycleRewardPool * CYCLE_22_PERCENT) /
                PERCENT_BPS;
            uint256 cycle69Reward = (cycleRewardPool * CYCLE_69_PERCENT) /
                PERCENT_BPS;
            _setCyclePayoutPool(DAY22, cycle22Reward);
            _setCyclePayoutPool(DAY69, cycle69Reward);
            _setCyclePayoutPool(
                DAY420,
                cycleRewardPool - cycle22Reward - cycle69Reward
            );
        }

        uint256 ethForDistribution = s_undistributedETH;

        //cycle ETH payout
        if (ethForDistribution != 0) {
            s_undistributedETH = 0;
            uint256 ethCycle22Reward = (ethForDistribution * CYCLE_22_PERCENT) /
                PERCENT_BPS;
            uint256 ethCycle69Reward = (ethForDistribution * CYCLE_69_PERCENT) /
                PERCENT_BPS;

            _setETHCyclePayoutPool(DAY22, ethCycle22Reward);
            _setETHCyclePayoutPool(DAY69, ethCycle69Reward);
            _setETHCyclePayoutPool(
                DAY420,
                ethForDistribution - ethCycle22Reward - ethCycle69Reward
            );
        }
    }

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

        protocolFee = getMintCost(mintPower, getCurrentMintCost());

        // Transfer Titanx From user to contract
        IERC20(s_titanxAddress).transferFrom(
            _msgSender(),
            address(this),
            protocolFee
        );

        s_undistributedTitanX += protocolFee;

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

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

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

        uint256 reward = getCyclePayoutPool(cycleNo);
        uint256 ethReward = getETHCyclePayoutPool(cycleNo);

        if (reward == 0 && ethReward == 0) return PayoutTriggered.NO;

        //calculate cycle reward per share and get new cycle Index
        _calculateCycleRewardPerShare(
            cycleNo,
            reward,
            ethReward,
            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 22, 69, 420
     */
    function _claimCyclePayout(
        uint256 cycleNo
    ) private returns (uint256, uint256) {
        (
            uint256 reward,
            uint256 ethRewards,
            uint256 userClaimCycleIndex,
            uint256 userClaimSharesIndex
        ) = calculateUserCycleReward(_msgSender(), cycleNo);
        _updateUserClaimIndexes(
            _msgSender(),
            cycleNo,
            userClaimCycleIndex,
            userClaimSharesIndex
        );
        return (reward, ethRewards);
    }

    /** @dev burn liquid Helios through other project.
     * called by other contracts for proof of burn 2.0 with up to 8% for both builder fee and user rebate
     * @param user user address
     * @param amount liquid helios amount
     * @param userRebatePercentage percentage for user rebate in liquid helios (0 - 8)
     * @param rewardPaybackPercentage percentage for builder fee in liquid helios (0 - 8)
     * @param rewardPaybackAddress builder can opt to receive fee in another address
     */
    function _burnLiquidHlx(
        address user,
        uint256 amount,
        uint256 userRebatePercentage,
        uint256 rewardPaybackPercentage,
        address rewardPaybackAddress
    ) private {
        if (amount == 0) revert Helios_InvalidAmount();
        if (balanceOf(user) < amount) revert Helios_InsufficientBalance();
        _spendAllowance(user, _msgSender(), amount);
        _burnbefore(userRebatePercentage, rewardPaybackPercentage);
        _burn(user, amount);
        _burnAfter(
            user,
            amount,
            userRebatePercentage,
            rewardPaybackPercentage,
            rewardPaybackAddress,
            BurnSource.LIQUID
        );
    }

    /** @dev burn stake through other project.
     * called by other contracts for proof of burn 2.0 with up to 8% for both builder fee and user rebate
     * @param user user address
     * @param id stake id
     * @param userRebatePercentage percentage for user rebate in liquid helios (0 - 8)
     * @param rewardPaybackPercentage percentage for builder fee in liquid helios (0 - 8)
     * @param rewardPaybackAddress builder can opt to receive fee in another address
     */
    function _burnStake(
        address user,
        uint256 id,
        uint256 userRebatePercentage,
        uint256 rewardPaybackPercentage,
        address rewardPaybackAddress
    ) private {
        _spendBurnStakeAllowance(user);
        _burnbefore(userRebatePercentage, rewardPaybackPercentage);
        _burnAfter(
            user,
            _endStake(
                user,
                id,
                getCurrentContractDay(),
                StakeAction.BURN,
                StakeAction.END_OWN,
                getGlobalPayoutTriggered()
            ),
            userRebatePercentage,
            rewardPaybackPercentage,
            rewardPaybackAddress,
            BurnSource.STAKE
        );
    }

    /** @dev burn mint through other project.
     * called by other contracts for proof of burn 2.0
     * burn mint has no builder reward and no user rebate
     * @param user user address
     * @param id mint id
     */
    function _burnMint(address user, uint256 id) private {
        _spendBurnMintAllowance(user);
        _burnbefore(0, 0);
        uint256 amount = _claimMint(user, id, MintAction.BURN);
        _mint(s_genesisAddress, (amount * 800) / PERCENT_BPS);
        _burnAfter(user, amount, 0, 0, _msgSender(), BurnSource.MINT);
    }

    /** @dev perform checks before burning starts.
     * check reward percentage and check if called by supported contract
     * @param userRebatePercentage percentage for user rebate
     * @param rewardPaybackPercentage percentage for builder fee
     */
    function _burnbefore(
        uint256 userRebatePercentage,
        uint256 rewardPaybackPercentage
    ) private view {
        if (
            rewardPaybackPercentage + userRebatePercentage >
            MAX_BURN_REWARD_PERCENT
        ) revert Helios_InvalidBurnRewardPercent();

        //Only supported contracts is allowed to call this function
        if (
            !IERC165(_msgSender()).supportsInterface(
                IERC165.supportsInterface.selector
            ) ||
            !IERC165(_msgSender()).supportsInterface(
                type(IHlxOnBurn).interfaceId
            )
        ) revert Helios_NotSupportedContract();
    }

    /** @dev update burn stats and mint reward to builder or user if applicable
     * @param user user address
     * @param amount helios amount burned
     * @param userRebatePercentage percentage for user rebate in liquid helios (0 - 8)
     * @param rewardPaybackPercentage percentage for builder fee in liquid helios (0 - 8)
     * @param rewardPaybackAddress builder can opt to receive fee in another address
     * @param source liquid/mint/stake
     */
    function _burnAfter(
        address user,
        uint256 amount,
        uint256 userRebatePercentage,
        uint256 rewardPaybackPercentage,
        address rewardPaybackAddress,
        BurnSource source
    ) private {
        _updateBurnAmount(user, _msgSender(), amount, source);

        uint256 devFee;
        uint256 userRebate;
        if (rewardPaybackPercentage != 0)
            devFee =
                (amount * rewardPaybackPercentage * PERCENT_BPS) /
                (100 * PERCENT_BPS);
        if (userRebatePercentage != 0)
            userRebate =
                (amount * userRebatePercentage * PERCENT_BPS) /
                (100 * PERCENT_BPS);

        if (devFee != 0) _mint(rewardPaybackAddress, devFee);
        if (userRebate != 0) _mint(user, userRebate);

        IHlxOnBurn(_msgSender()).onBurn(user, amount);
    }

    /** @dev Recommended method to transfer Tokens
     * @param to receiving address.
     * @param amount in wei.
     */
    function _sendTitanX(address to, uint256 amount) private {
        if (to == address(0)) revert Helios_InvalidAddress();
        IERC20(s_titanxAddress).transfer(to, amount);
    }

    /** @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 Helios_InvalidAddress();
        (bool sent, ) = to.call{value: amount}("");
        if (!sent) revert Helios_FailedToSendAmount();
    }

    /** @dev reduce user's allowance for caller (spender/project) by 1 (burn 1 stake at a time)
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     * @param user user address
     */
    function _spendBurnStakeAllowance(address user) private {
        uint256 currentAllowance = allowanceBurnStakes(user, _msgSender());
        if (currentAllowance != type(uint256).max) {
            if (currentAllowance == 0)
                revert Helios_InsufficientBurnAllowance();
            --s_allowanceBurnStakes[user][_msgSender()];
        }
    }

    /** @dev reduce user's allowance for caller (spender/project) by 1 (burn 1 mint at a time)
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     * @param user user address
     */
    function _spendBurnMintAllowance(address user) private {
        uint256 currentAllowance = allowanceBurnMints(user, _msgSender());
        if (currentAllowance != type(uint256).max) {
            if (currentAllowance == 0)
                revert Helios_InsufficientBurnAllowance();
            --s_allowanceBurnMints[user][_msgSender()];
        }
    }

    //Views
    /** @dev calculate user payout reward with specified cycle day and claim type (shares/burn).
     * it loops through all the unclaimed cylce index until the latest cycle index
     * @param user user address
     * @param cycleNo cycle day 7, 25, 69, 183, 420
     * @return rewards calculated reward
     * @return ethRewards calculated reward
     * @return userClaimCycleIndex last claim cycle index
     * @return userClaimSharesIndex last claim shares index
     */
    function calculateUserCycleReward(
        address user,
        uint256 cycleNo
    )
        public
        view
        returns (
            uint256 rewards,
            uint256 ethRewards,
            uint256 userClaimCycleIndex,
            uint256 userClaimSharesIndex
        )
    {
        uint256 cycleMaxIndex = getCurrentCycleIndex(cycleNo);

        (userClaimCycleIndex, userClaimSharesIndex) = getUserLastClaimIndex(
            user,
            cycleNo
        );
        uint256 sharesMaxIndex = getUserLatestShareIndex(user);

        for (uint256 i = userClaimCycleIndex; i <= cycleMaxIndex; i++) {
            (uint256 payoutPerShare, uint256 payoutDay) = getPayoutPerShare(
                cycleNo,
                i
            );
            (uint256 ethPayoutPerShare, ) = getETHPayoutPerShare(cycleNo, i);
            uint256 shares;
            (shares, userClaimSharesIndex) = _getSharesAndUpdateIndex(
                user,
                userClaimSharesIndex,
                sharesMaxIndex,
                payoutDay
            );
            if (payoutPerShare != 0 && shares != 0) {
                //reward has 18 decimals scaling, so here divide by 1e18
                rewards += (shares * payoutPerShare) / SCALING_FACTOR_1e18;
            }

            if (ethPayoutPerShare != 0 && shares != 0) {
                ethRewards +=
                    (shares * ethPayoutPerShare) /
                    SCALING_FACTOR_1e18;
            }

            userClaimCycleIndex = i + 1;
        }
    }

    function _getSharesAndUpdateIndex(
        address user,
        uint256 userClaimSharesIndex,
        uint256 sharesMaxIndex,
        uint256 payoutDay
    ) private view returns (uint256 shares, uint256) {
        //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;
        }
        return (shares, userClaimSharesIndex);
    }

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

    /** @notice get genesis Wallet Address
     * @return address
     */
    function getGenesisAddress() public view returns (address) {
        return s_genesisAddress;
    }

    /** @notice get Investment Address
     * @return address
     */
    function getInvestmentAddress() public view returns (address) {
        return s_investmentAddress;
    }

    /** @notice check if address is whitelisted
     * @return bool
     */
    function isWhiteListed(address contractAddress) public view returns (bool) {
        return s_whiteList[contractAddress];
    }

    /** @notice get total TitanX burned by user using this contract
     * @return total titan burned
     */
    function getTotalTitanXBurned() public view returns (uint256) {
        return s_totalTitanBurned;
    }

    /** @notice get contract TitanX balance
     * @return balance TitanX balance
     */
    function getTitanXBalance() public view returns (uint256) {
        return IERC20(s_titanxAddress).balanceOf(address(this));
    }

    /** @notice get contract Hlx balance
     * @return balance Hlx balance
     */
    function getHlxBalance() public view returns (uint256) {
        return balanceOf(address(this));
    }

    /** @notice get undistributed TitanX balance
     * @return amount titanX amount
     */
    function getUndistributedTitanX() public view returns (uint256) {
        return s_undistributedTitanX;
    }

    /** @notice get undistributed ETH balance
     * @return amount ETH
     */
    function getUndistributedETH() public view returns (uint256) {
        return s_undistributedETH;
    }

    /** @notice get estimated Hlx at end of miner
     * @return amount of hlx
     */
    function getMintableHlx(
        uint256 mintPower,
        uint256 numOfDays,
        uint256 titanToBurn,
        address user
    ) public view returns (uint256) {
        uint256 mintCost = getMintCost(mintPower, getCurrentMintCost());

        uint256 percentage = _calculateBonusPercentage(titanToBurn, mintCost);

        return
            calculateMintReward(
                mintPower,
                numOfDays,
                getCurrentMintableHlx(),
                getCurrentEAABonus(),
                getUserBurnAmplifierBonus(user),
                percentage
            );
    }

    /** @notice get estimated shares
     */
    function estimateShares(
        uint256 amount,
        uint256 numOfDays
    )
        public
        view
        returns (uint256 sharesWithBonus, uint256 sharesWithoutBonus)
    {
        uint256 shareRate = getCurrentShareRate();

        sharesWithBonus = calculateShares(amount, numOfDays, shareRate);

        sharesWithoutBonus = amount / (shareRate / SCALING_FACTOR_1e18);
    }

    /** @notice calculate share bonus
     * @return shareBonus calculated shares bonus in 11 decimals
     */
    function getShareBonus(uint256 noOfDays) public pure returns (uint256) {
        return calculateShareBonus(noOfDays);
    }

    /** @notice get user TitanX payout for all cycles
     * @param user user address
     * @return reward total reward
     */
    function getUserTitanXClaimableTotal(
        address user
    ) public view returns (uint256 reward) {
        uint256 _reward;

        (_reward, , , ) = calculateUserCycleReward(user, DAY22);
        reward += _reward;
        (_reward, , , ) = calculateUserCycleReward(user, DAY69);
        reward += _reward;
        (_reward, , , ) = calculateUserCycleReward(user, DAY420);
        reward += _reward;
    }

    /** @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, DAY22);
        reward += _reward;
        (, _reward, , ) = calculateUserCycleReward(user, DAY69);
        reward += _reward;
        (, _reward, , ) = calculateUserCycleReward(user, DAY420);
        reward += _reward;
    }

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

    /** @notice returns user's burn stakes allowance of a project
     * @param user user address
     * @param spender project address
     */
    function allowanceBurnStakes(
        address user,
        address spender
    ) public view returns (uint256) {
        return s_allowanceBurnStakes[user][spender];
    }

    /** @notice returns user's burn mints allowance of a project
     * @param user user address
     * @param spender project address
     */
    function allowanceBurnMints(
        address user,
        address spender
    ) public view returns (uint256) {
        return s_allowanceBurnMints[user][spender];
    }

    /** @notice Burn Helios tokens and creates Proof-Of-Burn record to be used by connected DeFi and fee is paid to specified address
     * @param user user address
     * @param amount helios amount
     * @param userRebatePercentage percentage for user rebate in liquid helios (0 - 8)
     * @param rewardPaybackPercentage percentage for builder fee in liquid helios (0 - 8)
     * @param rewardPaybackAddress builder can opt to receive fee in another address
     */
    function burnTokensToPayAddress(
        address user,
        uint256 amount,
        uint256 userRebatePercentage,
        uint256 rewardPaybackPercentage,
        address rewardPaybackAddress
    ) public dailyUpdate nonReentrant {
        _burnLiquidHlx(
            user,
            amount,
            userRebatePercentage,
            rewardPaybackPercentage,
            rewardPaybackAddress
        );
    }

    /** @notice Burn Hlx tokens and creates Proof-Of-Burn record to be used by connected DeFi and fee is paid to specified address
     * @param user user address
     * @param amount helios amount
     * @param userRebatePercentage percentage for user rebate in liquid helios (0 - 8)
     * @param rewardPaybackPercentage percentage for builder fee in liquid helios (0 - 8)
     */
    function burnTokens(
        address user,
        uint256 amount,
        uint256 userRebatePercentage,
        uint256 rewardPaybackPercentage
    ) public dailyUpdate nonReentrant {
        _burnLiquidHlx(
            user,
            amount,
            userRebatePercentage,
            rewardPaybackPercentage,
            _msgSender()
        );
    }

    /** @notice receive eth */
    receive() external payable {
        if (msg.value > 0) {
            s_undistributedETH += msg.value;
        }
    }

    /** @notice allows user to burn liquid helios directly from contract
     * @param amount helios amount
     */
    function userBurnTokens(uint256 amount) public dailyUpdate nonReentrant {
        if (amount == 0) revert Helios_InvalidAmount();
        if (balanceOf(_msgSender()) < amount)
            revert Helios_InsufficientBalance();
        _burn(_msgSender(), amount);
        _updateBurnAmount(_msgSender(), address(0), amount, BurnSource.LIQUID);
    }

    /** @notice Burn stake and creates Proof-Of-Burn record to be used by connected DeFi and fee is paid to specified address
     * @param user user address
     * @param id stake id
     * @param userRebatePercentage percentage for user rebate in liquid helios (0 - 8)
     * @param rewardPaybackPercentage percentage for builder fee in liquid helios (0 - 8)
     * @param rewardPaybackAddress builder can opt to receive fee in another address
     */
    function burnStakeToPayAddress(
        address user,
        uint256 id,
        uint256 userRebatePercentage,
        uint256 rewardPaybackPercentage,
        address rewardPaybackAddress
    ) public dailyUpdate nonReentrant {
        _burnStake(
            user,
            id,
            userRebatePercentage,
            rewardPaybackPercentage,
            rewardPaybackAddress
        );
    }

    /** @notice Burn stake and creates Proof-Of-Burn record to be used by connected DeFi and fee is paid to project contract address
     * @param user user address
     * @param id stake id
     * @param userRebatePercentage percentage for user rebate in liquid helios (0 - 8)
     * @param rewardPaybackPercentage percentage for builder fee in liquid helios (0 - 8)
     */
    function burnStake(
        address user,
        uint256 id,
        uint256 userRebatePercentage,
        uint256 rewardPaybackPercentage
    ) public dailyUpdate nonReentrant {
        _burnStake(
            user,
            id,
            userRebatePercentage,
            rewardPaybackPercentage,
            _msgSender()
        );
    }

    /** @notice allows user to burn stake directly from contract
     * @param id stake id
     */
    function userBurnStake(uint256 id) public dailyUpdate nonReentrant {
        _updateBurnAmount(
            _msgSender(),
            address(0),
            _endStake(
                _msgSender(),
                id,
                getCurrentContractDay(),
                StakeAction.BURN,
                StakeAction.END_OWN,
                getGlobalPayoutTriggered()
            ),
            BurnSource.STAKE
        );
    }

    /** @notice Burn mint and creates Proof-Of-Burn record to be used by connected DeFi.
     * Burn mint has no project reward or user rebate
     * @param user user address
     * @param id mint id
     */
    function burnMint(
        address user,
        uint256 id
    ) public dailyUpdate nonReentrant {
        _burnMint(user, id);
    }

    /** @notice allows user to burn mint directly from contract
     * @param id mint id
     */
    function userBurnMint(uint256 id) public dailyUpdate nonReentrant {
        _updateBurnAmount(
            _msgSender(),
            address(0),
            _claimMint(_msgSender(), id, MintAction.BURN),
            BurnSource.MINT
        );
    }

    /** @notice Sets `amount` as the allowance of `spender` over the caller's (user) mints.
     * @param spender contract address
     * @param amount allowance amount
     */
    function approveBurnMints(
        address spender,
        uint256 amount
    ) public returns (bool) {
        if (spender == address(0)) revert Helios_InvalidAddress();
        s_allowanceBurnMints[_msgSender()][spender] = amount;
        emit ApproveBurnMints(_msgSender(), spender, amount);
        return true;
    }

    /** @notice Sets `amount` as the allowance of `spender` over the caller's (user) stakes.
     * @param spender contract address
     * @param amount allowance amount
     */
    function approveBurnStakes(
        address spender,
        uint256 amount
    ) public returns (bool) {
        if (spender == address(0)) revert Helios_InvalidAddress();
        s_allowanceBurnStakes[_msgSender()][spender] = amount;
        emit ApproveBurnStakes(_msgSender(), spender, amount);
        return true;
    }
}

File 2 of 20 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol)

pragma solidity ^0.8.0;

import "../utils/introspection/IERC165.sol";

File 3 of 20 : 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 4 of 20 : 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 5 of 20 : 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 6 of 20 : 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 7 of 20 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (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;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

File 8 of 20 : 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 9 of 20 : BurnInfo.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

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

/**
 * @title BurnInfo
 * @dev this contract is meant to be inherited into main contract
 * @notice It has the variables and functions specifically for tracking burn amount and reward
 */

abstract contract BurnInfo {
    //Variables
    //track the total helios burn amount
    uint256 private s_totalHlxBurned;

    //mappings
    //track wallet address -> total helios burn amount
    mapping(address => uint256) private s_userBurnAmount;
    //track contract/project address -> total helios burn amount
    mapping(address => uint256) private s_project_BurnAmount;
    //track contract/project address, wallet address -> total helios burn amount
    mapping(address => mapping(address => uint256))
        private s_projectUser_BurnAmount;

    //events
    /** @dev log user burn helios event
     * project can be address(0) if user burns helios directly from helios contract
     * burnPoolCycleIndex is the cycle 28 index, which reuse the same index as Day 28 cycle index
     * helioSource 0=Liquid, 1=Mint, 2=Stake
     */
    event HlxBurned(
        address indexed user,
        address indexed project,
        uint256 amount,
        BurnSource helioSource
    );

    //functions
    /** @dev update the burn amount in each 28-cylce for user and project (if any)
     * @param user wallet address
     * @param project contract address
     * @param amount helios amount burned
     */
    function _updateBurnAmount(
        address user,
        address project,
        uint256 amount,
        BurnSource source
    ) internal {
        s_userBurnAmount[user] += amount;
        s_totalHlxBurned += amount;

        if (project != address(0)) {
            s_project_BurnAmount[project] += amount;
            s_projectUser_BurnAmount[project][user] += amount;
        }

        emit HlxBurned(user, project, amount, source);
    }

    /** @dev returned value is in 18 decimals, need to divide it by 1e18 and 100 (percentage) when using this value for reward calculation
     * The burn amplifier percentage is applied to all future mints. Capped at MAX_BURN_AMP_PERCENT (8%)
     * @param user wallet address
     * @return percentage returns percentage value in 18 decimals
     */
    function getUserBurnAmplifierBonus(
        address user
    ) public view returns (uint256) {
        uint256 userBurnTotal = getUserBurnTotal(user);
        if (userBurnTotal == 0) return 0;
        if (userBurnTotal >= MAX_BURN_AMP_BASE) return MAX_BURN_AMP_PERCENT;
        return (MAX_BURN_AMP_PERCENT * userBurnTotal) / MAX_BURN_AMP_BASE;
    }

    //views
    /** @notice return total burned helios amount from all users burn or projects burn
     * @return totalBurnAmount returns entire burned helios
     */
    function getTotalBurnTotal() public view returns (uint256) {
        return s_totalHlxBurned;
    }

    /** @notice return user address total burned helios
     * @return userBurnAmount returns user address total burned helios
     */
    function getUserBurnTotal(address user) public view returns (uint256) {
        return s_userBurnAmount[user];
    }

    /** @notice return project address total burned helios amount
     * @return projectTotalBurnAmount returns project total burned helios
     */
    function getProjectBurnTotal(
        address contractAddress
    ) public view returns (uint256) {
        return s_project_BurnAmount[contractAddress];
    }

    /** @notice return user address total burned helios amount via a project address
     * @param contractAddress project address
     * @param user user address
     * @return projectUserTotalBurnAmount returns user address total burned helios via a project address
     */
    function getProjectUserBurnTotal(
        address contractAddress,
        address user
    ) public view returns (uint256) {
        return s_projectUser_BurnAmount[contractAddress][user];
    }
}

File 10 of 20 : 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 420 ether and increases capped at 1500 ether */
    uint256 private s_currentshareRate;
    /** @dev mintCost starts 420m ether increases and capped at 2B ether, uin256 has enough size */
    uint256 private s_currentMintCost;
    /** @dev mintableHlx starts 4.2m ether decreases and capped at 420 ether, uint96 has enough size */
    uint256 private s_currentMintableHlx;
    /** @dev mintPowerBonus starts 350_000_000 and decreases capped at 35_000 */
    uint256 private s_currentMintPowerBonus;
    /** @dev EAABonus starts 10_000_000 and decreases to 0 */
    uint256 private s_currentEAABonus;

    /** @dev 7 day update for percentages */
    uint256 private s_nextSevenDayUpdate;

    /** @dev Percentage Share to BuynBurn */
    uint256 private s_percentBuynBurn;

    /** @dev Percentage to Treasury*/
    uint256 private s_percentTreasury;

    /** @dev track if any of the cycle day 22, 69, 420 has payout triggered succesfully
     * 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 22, 69, 420 when distributeTitanX() is called */
    mapping(uint256 => uint256) private s_cyclePayouts;

    /** @dev track payouts based on every cycle day 22, 69, 420 when distributeETH() is called */
    mapping(uint256 => uint256) private s_ethCyclePayouts;

    /** @dev track payout index for each cycle day, increased by 1 when triggerPayouts() is called succesfully
     *  eg. curent index is 2, s_cyclePayoutIndex[DAY22] = 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[DAY22][2].day = 22
     * s_CyclePayoutPerShare[DAY22][2].payoutPerShare = 0.1
     */
    mapping(uint256 => mapping(uint256 => CycleRewardPerShare))
        private s_cyclePayoutPerShare;

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

    /** @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][DAY22].cycleIndex = 1
     * [address][DAY22].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[DAY22] = 22
     *     s_nextCyclePayoutDay[DAY69] = 69
     */
    mapping(uint256 => uint256) s_nextCyclePayoutDay;

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

    struct UserCycleClaimIndex {
        uint256 cycleIndex;
        uint256 sharesIndex;
    }

    //event
    event GlobalDailyUpdateStats(
        uint256 indexed day,
        uint256 indexed mintCost,
        uint256 mintableHlx,
        uint256 mintPowerBonus,
        uint256 EAABonus
    );

    /** @dev Update variables in terms of day, modifier is used in all external/public functions (exclude view)
     * Every interaction to the contract would run this function to update variables
     */
    modifier dailyUpdate() {
        _dailyUpdate();
        _;
    }

    constructor() {
        i_genesisTs = block.timestamp;
        s_currentContractDay = 1;
        s_currentMintCost = START_MAX_MINT_COST;
        s_currentMintableHlx = START_MAX_MINTABLE_PER_DAY;
        s_currentshareRate = START_SHARE_RATE;
        s_currentMintPowerBonus = START_MINTPOWER_INCREASE_BONUS;
        s_currentEAABonus = EAA_START;
        s_nextCyclePayoutDay[DAY22] = DAY22;
        s_nextCyclePayoutDay[DAY69] = DAY69;
        s_nextCyclePayoutDay[DAY420] = DAY420;
        s_nextSevenDayUpdate = 7;
        s_percentBuynBurn = 60_00;
        s_percentTreasury = 10_00;
    }

    /** @dev calculate and update variables daily and reset triggers flag */
    function _dailyUpdate() 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 newMintableHlx = s_currentMintableHlx;
            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;
                newMintableHlx =
                    (newMintableHlx * DAILY_SUPPLY_MINTABLE_REDUCTION) /
                    PERCENT_BPS;
                newMintPowerBonus =
                    (newMintPowerBonus *
                        DAILY_MINTPOWER_INCREASE_BONUS_REDUCTION) /
                    PERCENT_BPS;

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

                if (
                    currentContractDay >= s_nextSevenDayUpdate &&
                    s_percentBuynBurn != PERCENT_TO_BUY_AND_BURN_FINAL &&
                    s_percentTreasury != PERCENT_TO_TREASURY_FINAL
                ) {
                    s_percentBuynBurn -= PERCENT_CHANGE;
                    s_percentTreasury += PERCENT_CHANGE;
                    s_nextSevenDayUpdate += 7;
                }

                if (newMintableHlx < CAPPED_MIN_DAILY_HLX_MINTABLE) {
                    newMintableHlx = CAPPED_MIN_DAILY_HLX_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 GlobalDailyUpdateStats(
                    ++currentContractDay,
                    newMintCost,
                    newMintableHlx,
                    newMintPowerBonus,
                    newEAABonus
                );
            }

            s_currentMintCost = newMintCost;
            s_currentMintableHlx = newMintableHlx;
            s_currentMintPowerBonus = newMintPowerBonus;
            s_currentEAABonus = 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[DAY22] != 0) {
                s_addressCycleToLastClaimIndex[user][DAY22].cycleIndex =
                    s_cyclePayoutIndex[DAY22] +
                    1;

                s_addressCycleToLastClaimIndex[user][DAY69].cycleIndex =
                    s_cyclePayoutIndex[DAY69] +
                    1;
                s_addressCycleToLastClaimIndex[user][DAY420]
                    .cycleIndex = uint96(s_cyclePayoutIndex[DAY420] + 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 cylce day 22, 69, 420
     * @param reward total accumulated reward in cycle day 22, 69, 420
     * @param globalActiveShares global active shares
     * @return index return latest current cycleIndex
     */
    function _calculateCycleRewardPerShare(
        uint256 cycleNo,
        uint256 reward,
        uint256 ethReward,
        uint256 globalActiveShares
    ) internal returns (uint256 index) {
        s_cyclePayouts[cycleNo] = 0;
        s_ethCyclePayouts[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();
        s_ethCyclePayoutPerShare[cycleNo][index].payoutPerShare =
            (ethReward * SCALING_FACTOR_1e18) /
            globalActiveShares;
        s_ethCyclePayoutPerShare[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 22, 69, 420
     * @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 = userClaimCycleIndex;

        if (
            userClaimSharesIndex !=
            s_addressCycleToLastClaimIndex[user][cycleNo].sharesIndex
        )
            s_addressCycleToLastClaimIndex[user][cycleNo]
                .sharesIndex = 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 22, 69, 420 pool
     * @param cycleNo cycle day 22, 69, 420
     * @param reward reward from distributeETH()
     */
    function _setCyclePayoutPool(uint256 cycleNo, uint256 reward) internal {
        s_cyclePayouts[cycleNo] += reward;
    }

    /** @dev add ETH reward into cycle day 22, 69, 420 pool
     * @param cycleNo cycle day 22, 69, 420
     * @param ethReward reward
     */
    function _setETHCyclePayoutPool(
        uint256 cycleNo,
        uint256 ethReward
    ) internal {
        s_ethCyclePayouts[cycleNo] += ethReward;
    }

    /** @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 22, 69, 420
     */
    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);
        }
    }

    //Public functions
    /** @notice allow anyone to sync dailyUpdate manually */
    function manualDailyUpdate() public dailyUpdate {}

    /** 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 Treasury Percentage
     * @return percentTreasury current day
     */
    function getTreasuryPercentage() public view returns (uint256) {
        return s_percentTreasury;
    }

    /** @notice Returns current BuynBurn Percentage
     * @return percentBuynBurn current  day
     */
    function getBuynBurnPercentage() public view returns (uint256) {
        return s_percentBuynBurn;
    }

    /** @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 Helios
     * @return currentMintableHlx current mintable Helios
     */
    function getCurrentMintableHlx() public view returns (uint256) {
        return s_currentMintableHlx;
    }

    /** @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 22, 69, 420
     * @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 22, 69, 420
     * @return currentPayoutPool current accumulated payout pool
     */
    function getCyclePayoutPool(uint256 cycleNo) public view returns (uint256) {
        return s_cyclePayouts[cycleNo];
    }

    /** @notice Returns the distributed ETH pool reward for the specified cycle day
     * @param cycleNo cycle day 22, 69, 420
     * @return currentPayoutPool current accumulated payout pool
     */
    function getETHCyclePayoutPool(
        uint256 cycleNo
    ) public view returns (uint256) {
        return s_ethCyclePayouts[cycleNo];
    }

    /** @notice Returns the calculated payout per share and contract day for the specified cycle day and index
     * @param cycleNo cycle day 22, 69, 420
     * @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 the calculated ETH payout per share and contract day for the specified cycle day and index
     * @param cycleNo cycle day 22, 69, 420
     * @param index cycle index
     * @return payoutPerShare calculated payout per share
     * @return triggeredDay the day when payout was triggered to perform calculation
     */
    function getETHPayoutPerShare(
        uint256 cycleNo,
        uint256 index
    ) public view returns (uint256, uint256) {
        return (
            s_ethCyclePayoutPerShare[cycleNo][index].payoutPerShare,
            s_ethCyclePayoutPerShare[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 22, 69, 420
     * @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 22, 69, 420
     * @return nextPayoutDay next payout day
     */
    function getNextCyclePayoutDay(
        uint256 cycleNo
    ) public view returns (uint256) {
        return s_nextCyclePayoutDay[cycleNo];
    }
}

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

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

//custom errors
error Helios_InvalidMintLength();
error Helios_InvalidMintPower();
error Helios_NoMintExists();
error Helios_MintHasClaimed();
error Helios_MintNotMature();
error Helios_MintHasBurned();

abstract contract MintInfo {
    //variables
    /** @dev track global hRank */
    uint256 private s_globalHRank;
    /** @dev track total mint claimed */
    uint256 private s_globalMintClaim;
    /** @dev track total mint burned */
    uint256 private s_globalMintBurn;
    /** @dev track total helios minting */
    uint256 private s_globalHlxMinting;
    /** @dev track total helios penalty */
    uint256 private s_globalHlxMintPenalty;
    /** @dev track global mint power */
    uint256 private s_globalMintPower;

    //mappings
    /** @dev track address => mintId */
    mapping(address => uint256) private s_addressMId;
    /** @dev track address, mintId => hRank info (gHrank, gMintPower) */
    mapping(address => mapping(uint256 => HRankInfo))
        private s_addressMIdToHRankInfo;
    /** @dev track global hRank => mintInfo*/
    mapping(uint256 => UserMintInfo) private s_hRankToMintInfo;

    //structs
    struct UserMintInfo {
        uint256 mintPower;
        uint16 numOfDays;
        uint256 mintableHlx;
        uint48 mintStartTs;
        uint48 maturityTs;
        uint256 mintPowerBonus;
        uint256 EAABonus;
        uint256 mintedHlx;
        uint256 mintCost;
        uint256 penalty;
        uint256 titanBurned;
        MintStatus status;
    }

    struct HRankInfo {
        uint256 hRank;
        uint256 gMintPower;
    }

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

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

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

    //functions
    /** @dev create a new mint
     * @param user user address
     * @param mintPower mint power
     * @param numOfDays mint lenght
     * @param mintableHlx mintable helios
     * @param mintPowerBonus mint power bonus
     * @param EAABonus EAA bonus
     * @param burnAmpBonus burn amplifier bonus
     * @param gMintPower global mint power
     * @param currentHRank current global hRank
     * @param mintCost actual mint cost paid for a mint
     * @param titanAmount titan Burned amount
     */
    function _startMint(
        address user,
        uint256 mintPower,
        uint256 numOfDays,
        uint256 mintableHlx,
        uint256 mintPowerBonus,
        uint256 EAABonus,
        uint256 burnAmpBonus,
        uint256 gMintPower,
        uint256 currentHRank,
        uint256 mintCost,
        uint256 titanAmount
    ) internal returns (uint256 mintable) {
        if (numOfDays == 0 || numOfDays > MAX_MINT_LENGTH)
            revert Helios_InvalidMintLength();
        if (mintPower == 0 || mintPower > MAX_MINT_POWER_CAP)
            revert Helios_InvalidMintPower();

        uint256 percentage = 0;

        if (titanAmount > 0) {
            percentage = _calculateBonusPercentage(titanAmount, mintCost);
        }

        //calculate mint reward up front with the provided params
        mintable = calculateMintReward(
            mintPower,
            numOfDays,
            mintableHlx,
            EAABonus,
            burnAmpBonus,
            percentage
        );

        _storeMintInfo(
            user,
            mintPower,
            numOfDays,
            mintable,
            mintPowerBonus,
            EAABonus,
            currentHRank,
            gMintPower,
            mintCost,
            titanAmount
        );
    }

    function _calculateBonusPercentage(
        uint256 titanAmount,
        uint256 mintCost
    ) internal pure returns (uint256) {
        uint256 percentage = (titanAmount * 10000) / mintCost;

        return percentage;
    }

    function _storeMintInfo(
        address user,
        uint256 mintPower,
        uint256 numOfDays,
        uint256 mintable,
        uint256 mintPowerBonus,
        uint256 EAABonus,
        uint256 currentHRank,
        uint256 gMintPower,
        uint256 mintCost,
        uint256 titanAmount
    ) private {
        //store variables into mint info
        UserMintInfo memory userMintInfo = UserMintInfo({
            mintPower: mintPower,
            numOfDays: uint16(numOfDays),
            mintableHlx: mintable,
            mintPowerBonus: mintPowerBonus,
            EAABonus: EAABonus,
            mintStartTs: uint48(block.timestamp),
            maturityTs: uint48(block.timestamp + (numOfDays * SECONDS_IN_DAY)),
            mintedHlx: 0,
            mintCost: mintCost,
            penalty: 0,
            titanBurned: titanAmount,
            status: MintStatus.ACTIVE
        });

        /** s_addressMId[user] tracks mintId for each addrress
         * s_addressMIdToHRankInfo[user][id] tracks current mint hRank and gPowerMint
         *  s_hRankToMintInfo[currentHRank] stores mint info
         */
        uint256 id = ++s_addressMId[user];
        s_addressMIdToHRankInfo[user][id].hRank = currentHRank;
        s_addressMIdToHRankInfo[user][id].gMintPower = gMintPower;
        s_hRankToMintInfo[currentHRank] = userMintInfo;

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

    /** @dev update variables
     * @param currentHRank current hRank
     * @param gMintPower current global mint power
     * @param gMinting current global minting
     */
    function _updateMintStats(
        uint256 currentHRank,
        uint256 gMintPower,
        uint256 gMinting
    ) internal {
        s_globalHRank = currentHRank;
        s_globalMintPower = gMintPower;
        s_globalHlxMinting = gMinting;
    }

    /** @dev calculate reward for claim mint or burn mint.
     * Claim mint has maturity check while burn mint would bypass maturity check.
     * @param user user address
     * @param id mint id
     * @param action claim mint or burn 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 hRank = s_addressMIdToHRankInfo[user][id].hRank;
        uint256 gMintPower = s_addressMIdToHRankInfo[user][id].gMintPower;
        if (hRank == 0) revert Helios_NoMintExists();

        UserMintInfo memory mint = s_hRankToMintInfo[hRank];
        if (mint.status == MintStatus.CLAIMED) revert Helios_MintHasClaimed();
        if (mint.status == MintStatus.BURNED) revert Helios_MintHasBurned();

        //Only check maturity for claim mint action, burn mint bypass this check
        if (mint.maturityTs > block.timestamp && action == MintAction.CLAIM)
            revert Helios_MintNotMature();

        s_globalHlxMinting -= mint.mintableHlx;
        reward = _calculateClaimReward(user, hRank, gMintPower, mint, action);
    }

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

        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 1e7, so divide by 1e7
        reward =
            uint256(userMintInfo.mintableHlx) +
            (bonus / SCALING_FACTOR_1e7);
        penaltyAmount = (reward * penalty) / 100;
        reward -= penaltyAmount;

        if (action == MintAction.CLAIM) ++s_globalMintClaim;
        if (action == MintAction.BURN) ++s_globalMintBurn;
        if (penaltyAmount != 0) s_globalHlxMintPenalty += penaltyAmount;

        //only stored minted amount for claim mint
        if (action == MintAction.CLAIM) {
            s_hRankToMintInfo[hRank].mintedHlx = reward;
            s_hRankToMintInfo[hRank].penalty = penaltyAmount;
        }

        emit MintClaimed(user, hRank, 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];
    }

    /**
     * @dev Estimates the reward for a specific mint operation for a user, including any applicable bonuses and subtracting penalties for late claims.
     * This function calculates an estimate of the total reward a user can expect from a mint at the time of its maturity, based on the current state.
     *
     * @param user The address of the user who initiated the mint operation.
     * @param mintId The unique identifier of the mint operation for which the reward is being estimated.
     */
    function estimateMintReward(
        address user,
        uint256 mintId
    ) public view returns (uint256 baseReward) {
        uint256 hRank = s_addressMIdToHRankInfo[user][mintId].hRank;
        uint256 gMintPower = s_addressMIdToHRankInfo[user][mintId].gMintPower;
        if (hRank == 0) revert Helios_NoMintExists();

        UserMintInfo memory mint = s_hRankToMintInfo[hRank];
        // Base mintable HLX
        baseReward = mint.mintableHlx;

        // Calculate additional rewards here.
        uint256 bonus = calculateMintPowerBonus(
            mint.mintPowerBonus,
            mint.mintPower,
            gMintPower,
            s_globalMintPower
        );
        baseReward += baseReward + (bonus / SCALING_FACTOR_1e7); //hypothetical bonus
    }

    /** @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_hRankToMintInfo[s_addressMIdToHRankInfo[user][id].hRank];
    }

    /** @notice Return all mints info of an address
     * @param user address
     * @return mintInfos all mints info of an address including mint id, hRank 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,
                hRank: s_addressMIdToHRankInfo[user][i].hRank,
                gMintPower: s_addressMIdToHRankInfo[user][i].gMintPower,
                mintInfo: getUserMintInfo(user, i)
            });
        }
    }

    /** @notice Return total mints burned
     * @return totalMintBurned total mints burned
     */
    function getTotalMintBurn() public view returns (uint256) {
        return s_globalMintBurn;
    }

    /** @notice Return current gobal hRank
     * @return globalHRank global hRank
     */
    function getGlobalHRank() public view returns (uint256) {
        return s_globalHRank;
    }

    /** @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 active mints (exluded claimed and burned mints)
     * @return totalActiveMints total active mints
     */
    function getTotalActiveMints() public view returns (uint256) {
        return s_globalHRank - s_globalMintClaim - s_globalMintBurn;
    }

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

    /** @notice Return total helios penalty
     * @return totalHlxPenalty total helios penalty
     */
    function getTotalMintPenalty() public view returns (uint256) {
        return s_globalHlxMintPenalty;
    }
}

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

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

error Helios_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 Helios_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 13 of 20 : StakeInfo.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

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

//custom errors
error Helios_InvalidStakeLength();
error Helios_RequireOneMinimumShare();
error Helios_ExceedMaxAmountPerStake();
error Helios_NoStakeExists();
error Helios_StakeHasEnded();
error Helios_StakeNotMatured();
error Helios_StakeHasBurned();
error Helios_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 Helios */
    uint256 private s_globalHlxStaked;
    /** @dev track global end stake penalty */
    uint256 private s_globalStakePenalty;
    /** @dev track global ended stake */
    uint256 private s_globalStakeEnd;
    /** @dev track global burned stake */
    uint256 private s_globalStakeBurn;

    //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 {
        uint256 hlxAmount;
        uint256 shares;
        uint16 numOfDays;
        uint48 stakeStartTs;
        uint48 maturityTs;
        uint256 titanBurned;
        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 userStakeInfo
    );

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

    //functions
    /** @dev create a new stake
     * @param user user address
     * @param amount helios amount
     * @param numOfDays stake lenght
     * @param shareRate current share rate
     * @param day current contract day
     * @param isPayoutTriggered has global payout triggered
     * @param titanAmount titan amount burned
     * @param titanPrice titan price against hlx
     * @return isFirstShares first created shares or not
     */
    function _startStake(
        address user,
        uint256 amount,
        uint256 numOfDays,
        uint256 shareRate,
        uint256 day,
        PayoutTriggered isPayoutTriggered,
        uint256 titanAmount,
        uint256 titanPrice
    ) internal returns (uint256 isFirstShares) {
        uint256 sId = ++s_addressSId[user];
        if (sId > MAX_STAKE_PER_WALLET) revert Helios_MaxedWalletStakes();
        if (numOfDays < MIN_STAKE_LENGTH || numOfDays > MAX_STAKE_LENGTH)
            revert Helios_InvalidStakeLength();

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

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

        if (titanAmount > 0) {
            uint256 percentage = calculateBonusPercentage(
                titanAmount,
                titanPrice,
                amount
            );
            if (percentage > BURN_STAKE_AMP) percentage = BURN_STAKE_AMP;

            shares = shares + ((shares * percentage) / PERCENT_BASE);
        }

        _storeUserStakesInfo(sId, user, amount, numOfDays, shares, titanAmount);
        //update shares changes
        isFirstShares = _updateSharesStats(
            user,
            shares,
            amount,
            day,
            isPayoutTriggered,
            StakeAction.START
        );
    }

    /**
     * @dev Calculates the bonus percentage based on the amount of Titan tokens burned.
     * @param titanAmount The amount of Titan tokens burned by the user.
     * @param titanPrice The price of Titan tokens relative to Helios.
     * @param amountStaked The amount of Helios staked by the user.
     * @return The bonus percentage, scaled to maintain precision.
     *
     * This function calculates the value of the burned Titan tokens in terms of the staked Helios tokens.
     * It then computes the bonus percentage based on this value. The result is scaled to account for
     * Solidity's lack of support for floating-point arithmetic.
     */
    function calculateBonusPercentage(
        uint256 titanAmount,
        uint256 titanPrice,
        uint256 amountStaked
    ) internal pure returns (uint256) {
        uint256 titanValueInHlx = (amountStaked * titanPrice) /
            SCALING_FACTOR_1e18;

        uint256 percentage = (titanAmount * PERCENT_BASE) / titanValueInHlx;

        return percentage;
    }

    /**
     * @dev Records stake information for a user.
     * @param sId Unique identifier for the stake.
     * @param user Address of the user staking the tokens.
     * @param amount Amount of tokens staked.
     * @param numOfDays Duration of the stake in days.
     * @param shares Number of shares allocated for the stake.
     */
    function _storeUserStakesInfo(
        uint256 sId,
        address user,
        uint256 amount,
        uint256 numOfDays,
        uint256 shares,
        uint256 titanAmount
    ) private {
        uint256 currentGStakeId = ++s_globalStakeId;
        uint256 maturityTs;

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

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

        /** 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;

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

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

        UserStakeInfo memory userStakeInfo = s_globalStakeIdToStakeInfo[
            globalStakeId
        ];
        if (userStakeInfo.status == StakeStatus.ENDED)
            revert Helios_StakeHasEnded();
        if (userStakeInfo.status == StakeStatus.BURNED)
            revert Helios_StakeHasBurned();
        //end stake for others requires matured stake to prevent EES for others
        if (
            payOther == StakeAction.END_OTHER &&
            block.timestamp < userStakeInfo.maturityTs
        ) revert Helios_StakeNotMatured();

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

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

        helios = _calculatePrinciple(
            user,
            globalStakeId,
            userStakeInfo,
            action
        );
    }

    /** @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 helios 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_globalHlxStaked += amount;
        } else {
            s_addressIdToActiveShares[user][++index].activeShares =
                previousShares -
                shares;
            s_globalExpiredShares += shares;
            s_globalHlxStaked -= 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 = 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
     * @param action end stake or burn stake
     * @return principle calculated principle after penalty (if any)
     */
    function _calculatePrinciple(
        address user,
        uint256 globalStakeId,
        UserStakeInfo memory userStakeInfo,
        StakeAction action
    ) internal returns (uint256 principle) {
        uint256 hlxAmount = userStakeInfo.hlxAmount;
        //penalty is in percentage
        uint256 penalty = calculateEndStakePenalty(
            userStakeInfo.stakeStartTs,
            userStakeInfo.maturityTs,
            block.timestamp,
            action
        );

        uint256 penaltyAmount;
        penaltyAmount = (hlxAmount * penalty) / 100;
        principle = hlxAmount - 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 helios staked
     * @return totalHlxStaked total helios staked
     */
    function getTotalHlxStaked() public view returns (uint256) {
        return s_globalHlxStaked;
    }

    /** @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 stake burned
     * @return totalStakeBurned total stake burned
     */
    function getTotalStakeBurn() public view returns (uint256) {
        return s_globalStakeBurn;
    }

    /** @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: s_addressSIdToGlobalStakeId[user][i],
                stakeInfo: getUserStakeInfo(user, i)
            });
        }

        return stakes;
    }
}

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

interface IBuynBurn {
    function getCurrentTitanPrice() external view returns (uint256);
}

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

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

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

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

File 17 of 20 : ITITANX.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

interface ITITANX {

    // Enum for stake status
    enum StakeStatus {
        ACTIVE,
        ENDED,
        BURNED
    }

    // Struct for user stake information
    struct UserStakeInfo {
        uint152 titanAmount;
        uint128 shares;
        uint16 numOfDays;
        uint48 stakeStartTs;
        uint48 maturityTs;
        StakeStatus status;
    }

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

    function balanceOf(address account) external view returns (uint256);

    function getBalance() external;

    function mintLPTokens() external;

    function burnLPTokens() external;

    function startStake(uint256 amount, uint256 numOfDays) external;

    function endStake(uint256 id) external;

    function claimUserAvailableETHPayouts() external;
     
    function burnTokensToPayAddress(
        address user,
        uint256 amount,
        uint256 userRebatePercentage,
        uint256 rewardPaybackPercentage,
        address rewardPaybackAddress
    ) external;

    /** @notice get stake info with stake id
     * @return stakeInfo stake info
     */
    function getUserStakeInfo(
        address user,
        uint256 id
    ) external view returns (UserStakeInfo memory);

    /**
     * @notice Calculates the total ETH claimable by a user for all cycles.
     * @dev This function sums up the rewards from various cycles based on user shares.
     * @param user The address of the user for whom to calculate the claimable ETH.
     * @return reward The total ETH reward claimable by the user.
     */
    function getUserETHClaimableTotal(
        address user
    ) external view returns (uint256 reward);

    /**
     * @notice Get all stake info of a given user address.
     * @param user The address of the user to query stake information for.
     * @return An array of UserStake structs containing all stake info for the given address.
     */
    function getUserStakes(
        address user
    ) external view returns (UserStake[] memory);

    /**
     * @notice Trigger cycle payouts for days 8, 28, 90, 369, 888, including the burn reward cycle 28.
     * Payouts can be triggered on or after the maturity day of each cycle (e.g., Cycle8 on day 8).
     */
    function triggerPayouts() external;
}

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

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

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

//MintInfo

/** @notice the formula to calculate mint reward at create new mint
 * @param mintPower mint power 1 - 100,000
 * @param numOfDays mint length 1 - 250
 * @param mintableHlx current contract day mintable helios
 * @param EAABonus current contract day EAA Bonus
 * @param burnAmpBonus user burn amplifier bonus from getUserBurnAmplifierBonus(user)
 * @return reward base helios amount
 */
function calculateMintReward(
    uint256 mintPower,
    uint256 numOfDays,
    uint256 mintableHlx,
    uint256 EAABonus,
    uint256 burnAmpBonus,
    uint256 percentageBonus
) pure returns (uint256 reward) {
    uint256 baseReward = (mintableHlx * 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);
    }

    if (burnAmpBonus != 0) {
        //burnAmpBonus has 1e18 scaling
        reward += (baseReward * burnAmpBonus) / 100 / SCALING_FACTOR_1e18;
    }

    // Apply the percentage bonus
    if (percentageBonus != 0) {
        
        percentageBonus = percentageBonus > BURN_MINT_AMP ? BURN_MINT_AMP : percentageBonus;
        // Convert the bonus to a percentage (1000 represents 10%, so divide by 10000)
        uint256 additionalReward = (reward * percentageBonus) / 10000;
        reward += additionalReward;
    }

    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,000 from mintinfo
 * @param gMintPower global mint power from mintinfo
 * @param globalMintPower current global mint power
 * @return bonus bonus amount in helios
 */
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);
}

/** @notice Return max mint length
 * @return maxMintLength max mint length
 */
function getMaxMintDays() pure returns (uint256) {
    return MAX_MINT_LENGTH;
}

/** @notice Return max mints per wallet
 * @return maxMintPerWallet max mints per wallet
 */
function getMaxMintsPerWallet() pure returns (uint256) {
    return MAX_MINT_PER_WALLET;
}

/**
 * @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 Helios_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 helios 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(noOfDays)) / SCALING_FACTOR_1e11;
    shares /= (shareRate / SCALING_FACTOR_1e18);
    return shares;
}

/** @notice calculate share bonus
 * @param noOfDays stake length
 * @return shareBonus calculated shares bonus in 11 decimals
 */
function calculateShareBonus(uint256 noOfDays) pure returns (uint256 shareBonus) {
    if (noOfDays <= MIN_STAKE_LENGTH) {
        
        return SCALING_FACTOR_1e6; // no bonus
    }

    uint256 effectiveDays = noOfDays - MIN_STAKE_LENGTH;
    uint256 cappedEffectiveDays = effectiveDays <= (LPB_MAX_DAYS - MIN_STAKE_LENGTH) ? effectiveDays : (LPB_MAX_DAYS - MIN_STAKE_LENGTH);
    shareBonus = ((cappedEffectiveDays * SCALING_FACTOR_1e11) / LPB_PER_PERCENT);
    return shareBonus;
}


/** @notice calculate end stake penalty
 * @param stakeStartTs start stake timestamp
 * @param maturityTs  maturity timestamp
 * @param currentBlockTs current block timestamp
 * @param action end stake or burn stake
 * @return penalty penalty in percentage
 */
function calculateEndStakePenalty(
    uint256 stakeStartTs,
    uint256 maturityTs,
    uint256 currentBlockTs,
    StakeAction action
) 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);
    }

    //burn stake is excluded from penalty
    //if not matured and action is burn stake then return 0
    if (action == StakeAction.BURN) return 0;

    //Emergency End Stake
    //Not allow to EES below 50% maturity
    if (block.timestamp < stakeStartTs + (maturityTs - stakeStartTs) / 2)
        revert Helios_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 19 of 20 : constant.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "../interfaces/ITitanOnBurn.sol";


// ===================== 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;

// ===================== Helios ==========================================
uint256 constant PERCENT_TO_BUY_AND_BURN_FINAL = 0;
uint256 constant PERCENT_TO_CYCLE_PAYOUTS = 28_00;
uint256 constant PERCENT_TO_TREASURY_FINAL = 70_00;
uint256 constant PERCENT_CHANGE = 50;
uint256 constant PERCENT_TO_GENESIS = 2_00;

uint256 constant INCENTIVE_FEE_PERCENT = 3_000; //0.3%  
uint256 constant INCENTIVE_FEE_PERCENT_BASE = 1_000_000;

uint256 constant INITAL_LP_TOKENS = 1_600_000_000 ether; 

// ===================== globalInfo ==========================================
//Helios Supply Variables
uint256 constant START_MAX_MINTABLE_PER_DAY = 4_200_000_000 ether;
uint256 constant CAPPED_MIN_DAILY_HLX_MINTABLE = 420_000 ether;
uint256 constant DAILY_SUPPLY_MINTABLE_REDUCTION = 99_65;


//10% - 0% linear for 69 days
//EAA Variables
uint256 constant EAA_START = 10 * SCALING_FACTOR_1e6;
uint256 constant EAA_BONUSE_FIXED_REDUCTION_PER_DAY = 144_927;
uint256 constant EAA_END = 0;
uint256 constant MAX_BONUS_DAY = 69;

//Mint Cost Variables
uint256 constant START_MAX_MINT_COST = 420_000_000_000 ether;
uint256 constant CAPPED_MAX_MINT_COST = 2_000_000_000_000 ether;
uint256 constant DAILY_MINT_COST_INCREASE_STEP = 100_10;//0.1%

// 1000 to 0.1 HLX -0.35% Daily
//mintPower Bonus Variables
uint256 constant START_MINTPOWER_INCREASE_BONUS = 10_000 * SCALING_FACTOR_1e7; //starts at 10_000 with 1e7 scaling factor
uint256 constant CAPPED_MIN_MINTPOWER_BONUS = 10_000 * SCALING_FACTOR_1e3; //capped min of 0.1 * 1e7 = 10_000 * 1e3
uint256 constant DAILY_MINTPOWER_INCREASE_BONUS_REDUCTION = 99_65;

//Share Rate Variables
uint256 constant START_SHARE_RATE = 420 ether;

//Cycle Variables
uint256 constant DAY22 = 22;
uint256 constant DAY69 = 69;
uint256 constant DAY420 = 420;
uint256 constant CYCLE_22_PERCENT = 35_00;
uint256 constant CYCLE_69_PERCENT = 30_00;
uint256 constant CYCLE_420_PERCENT = 35_00;
uint256 constant PERCENT_BPS = 100_00;

// ===================== mintInfo ==========================================
uint256 constant MAX_MINT_POWER_CAP = 100_000;
uint256 constant MAX_MINT_LENGTH = 250;
uint256 constant CLAIM_MINT_GRACE_PERIOD = 7;
uint256 constant MAX_MINT_PER_WALLET = 1000;
uint256 constant MAX_BURN_AMP_BASE = 80 * 1e9 * 1 ether;
uint256 constant MAX_BURN_AMP_PERCENT = 8 ether;
uint256 constant MINT_DAILY_REDUCTION = 11; 

// ===================== stakeInfo ==========================================
uint256 constant MAX_STAKE_PER_WALLET = 1000;
uint256 constant MIN_STAKE_LENGTH = 30;
uint256 constant MAX_STAKE_LENGTH = 830;
uint256 constant END_STAKE_GRACE_PERIOD = 7;



// 0%-200% linear 1-830 days
/* Stake Longer Pays Better bonus */
uint256 constant LPB_MAX_DAYS = 830;
uint256 constant LPB_PER_PERCENT = 400;

//20%
/* Burn Stake Amplifier */
uint256 constant BURN_STAKE_AMP = 2000;

//10% 
/* Burn Mint Amplifier */
uint256 constant BURN_MINT_AMP = 1000;


// ===================== burnInfo ==========================================
uint256 constant MAX_BURN_REWARD_PERCENT = 8;

// ===================== Treasury ==========================================
uint256 constant PERCENT_TO_STAKERS = 10_00;
uint256 constant PERCENT_TO_BUYANDBURNHELIOS = 70_00;
uint16 constant STAKE_DURATION = 3500;

uint24 constant POOLFEE1PERCENT = 10000; //1% Fee
uint160 constant MIN_SQRT_RATIO = 4295128739;
uint160 constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;

address constant TITANX = 0xF19308F923582A6f7c465e5CE7a9Dc1BEC6665B1;
address constant UNISWAPV3FACTORY = 0x1F98431c8aD98523631AE4a59f267346ea31F984;
address constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address constant TITANX_WETH_POOL = 0xc45A81BC23A64eA556ab4CdF08A86B61cdcEEA8b;
address constant NONFUNGIBLEPOSITIONMANAGER = 0xC36442b4a4522E871399CD717aBDD847Ab11FE88;

uint8 constant BURN_REWARD_PERCENT_EACH = 4;

uint256 constant TREASURY_INCENTIVE_FEE_PERCENT = 1000;

uint256 constant INCENTIVE_FEE_CAP_ETH = 0.1 ether;

uint256 constant PERCENT_BASE = 10_000;

/*
    bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
*/
bytes4 constant INTERFACE_ID_ERC165 = 0x01ffc9a7;

// ERC-165 Interface ID for ITitanOnBurn
bytes4 constant INTERFACE_ID_ITITANONBURN =
    type(ITitanOnBurn).interfaceId;

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

enum MintAction {
    CLAIM,
    BURN
}
enum MintStatus {
    ACTIVE,
    CLAIMED,
    BURNED
}
enum StakeAction {
    START,
    END,
    BURN,
    END_OWN,
    END_OTHER
}
enum StakeStatus {
    ACTIVE,
    ENDED,
    BURNED
}
enum PayoutTriggered {
    NO,
    YES
}
enum InitialLPMinted {
    NO,
    YES
}
enum PayoutClaim {
    SHARES,
    BURN
}
enum BurnSource {
    LIQUID,
    MINT,
    STAKE
}
enum BurnPoolEnabled {
    FALSE,
    TRUE
}

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

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"genesisAddress","type":"address"},{"internalType":"address","name":"buyAndBurnAddress","type":"address"},{"internalType":"address","name":"titanxAddress","type":"address"},{"internalType":"address","name":"treasuryAddress","type":"address"},{"internalType":"address","name":"investmentAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Helios_AtLeastHalfMaturity","type":"error"},{"inputs":[],"name":"Helios_EmptyUndistributeFees","type":"error"},{"inputs":[],"name":"Helios_FailedToSendAmount","type":"error"},{"inputs":[],"name":"Helios_InsufficientBalance","type":"error"},{"inputs":[],"name":"Helios_InsufficientBurnAllowance","type":"error"},{"inputs":[],"name":"Helios_InvalidAddress","type":"error"},{"inputs":[],"name":"Helios_InvalidAmount","type":"error"},{"inputs":[],"name":"Helios_InvalidBurnRewardPercent","type":"error"},{"inputs":[],"name":"Helios_InvalidMintLength","type":"error"},{"inputs":[],"name":"Helios_InvalidMintPower","type":"error"},{"inputs":[],"name":"Helios_InvalidStakeLength","type":"error"},{"inputs":[],"name":"Helios_LPTokensHasMinted","type":"error"},{"inputs":[],"name":"Helios_MaxedWalletMints","type":"error"},{"inputs":[],"name":"Helios_MaxedWalletStakes","type":"error"},{"inputs":[],"name":"Helios_MintHasBurned","type":"error"},{"inputs":[],"name":"Helios_MintHasClaimed","type":"error"},{"inputs":[],"name":"Helios_MintNotMature","type":"error"},{"inputs":[],"name":"Helios_NoCycleRewardToClaim","type":"error"},{"inputs":[],"name":"Helios_NoMintExists","type":"error"},{"inputs":[],"name":"Helios_NoSharesExist","type":"error"},{"inputs":[],"name":"Helios_NoStakeExists","type":"error"},{"inputs":[],"name":"Helios_NotAllowed","type":"error"},{"inputs":[],"name":"Helios_NotOnwer","type":"error"},{"inputs":[],"name":"Helios_NotSupportedContract","type":"error"},{"inputs":[],"name":"Helios_OnlyBuyAndBurn","type":"error"},{"inputs":[],"name":"Helios_RequireOneMinimumShare","type":"error"},{"inputs":[],"name":"Helios_StakeHasBurned","type":"error"},{"inputs":[],"name":"Helios_StakeHasEnded","type":"error"},{"inputs":[],"name":"Helios_StakeNotMatured","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":"user","type":"address"},{"indexed":true,"internalType":"address","name":"project","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ApproveBurnMints","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"project","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ApproveBurnStakes","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":"uint256","name":"day","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"mintCost","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mintableHlx","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mintPowerBonus","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"EAABonus","type":"uint256"}],"name":"GlobalDailyUpdateStats","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"project","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"enum BurnSource","name":"helioSource","type":"uint8"}],"name":"HlxBurned","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"hRank","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":"hRank","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"gMintpower","type":"uint256"},{"components":[{"internalType":"uint256","name":"mintPower","type":"uint256"},{"internalType":"uint16","name":"numOfDays","type":"uint16"},{"internalType":"uint256","name":"mintableHlx","type":"uint256"},{"internalType":"uint48","name":"mintStartTs","type":"uint48"},{"internalType":"uint48","name":"maturityTs","type":"uint48"},{"internalType":"uint256","name":"mintPowerBonus","type":"uint256"},{"internalType":"uint256","name":"EAABonus","type":"uint256"},{"internalType":"uint256","name":"mintedHlx","type":"uint256"},{"internalType":"uint256","name":"mintCost","type":"uint256"},{"internalType":"uint256","name":"penalty","type":"uint256"},{"internalType":"uint256","name":"titanBurned","type":"uint256"},{"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":"ProtocolFeeRecevied","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"reward","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"ethReward","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":"hlxAmount","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":"uint256","name":"hlxAmount","type":"uint256"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint16","name":"numOfDays","type":"uint16"},{"internalType":"uint48","name":"stakeStartTs","type":"uint48"},{"internalType":"uint48","name":"maturityTs","type":"uint48"},{"internalType":"uint256","name":"titanBurned","type":"uint256"},{"internalType":"enum StakeStatus","name":"status","type":"uint8"}],"indexed":false,"internalType":"struct StakeInfo.UserStakeInfo","name":"userStakeInfo","type":"tuple"}],"name":"StakeStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TitanXDistributed","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":"user","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowanceBurnMints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowanceBurnStakes","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":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approveBurnMints","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approveBurnStakes","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":"burnLPTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"burnMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"userRebatePercentage","type":"uint256"},{"internalType":"uint256","name":"rewardPaybackPercentage","type":"uint256"}],"name":"burnStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"userRebatePercentage","type":"uint256"},{"internalType":"uint256","name":"rewardPaybackPercentage","type":"uint256"},{"internalType":"address","name":"rewardPaybackAddress","type":"address"}],"name":"burnStakeToPayAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"userRebatePercentage","type":"uint256"},{"internalType":"uint256","name":"rewardPaybackPercentage","type":"uint256"}],"name":"burnTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"userRebatePercentage","type":"uint256"},{"internalType":"uint256","name":"rewardPaybackPercentage","type":"uint256"},{"internalType":"address","name":"rewardPaybackAddress","type":"address"}],"name":"burnTokensToPayAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"cycleNo","type":"uint256"}],"name":"calculateUserCycleReward","outputs":[{"internalType":"uint256","name":"rewards","type":"uint256"},{"internalType":"uint256","name":"ethRewards","type":"uint256"},{"internalType":"uint256","name":"userClaimCycleIndex","type":"uint256"},{"internalType":"uint256","name":"userClaimSharesIndex","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"claimMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimUserAvailablePayouts","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":"distributeTitanX","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"endStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"endStakeForOthers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"mintId","type":"uint256"}],"name":"estimateMintReward","outputs":[{"internalType":"uint256","name":"baseReward","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"numOfDays","type":"uint256"}],"name":"estimateShares","outputs":[{"internalType":"uint256","name":"sharesWithBonus","type":"uint256"},{"internalType":"uint256","name":"sharesWithoutBonus","type":"uint256"}],"stateMutability":"view","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":"getBuynBurnPercentage","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":"getCurrentMintableHlx","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":[{"internalType":"uint256","name":"cycleNo","type":"uint256"}],"name":"getETHCyclePayoutPool","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"cycleNo","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getETHPayoutPerShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGenesisAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"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":"getGlobalHRank","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":[],"name":"getHlxBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getInvestmentAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"mintPower","type":"uint256"},{"internalType":"uint256","name":"numOfDays","type":"uint256"},{"internalType":"uint256","name":"titanToBurn","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"getMintableHlx","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":[{"internalType":"address","name":"contractAddress","type":"address"}],"name":"getProjectBurnTotal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"address","name":"user","type":"address"}],"name":"getProjectUserBurnTotal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"noOfDays","type":"uint256"}],"name":"getShareBonus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getTitanXBalance","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":"getTotalBurnTotal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalHlxStaked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalMintBurn","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":"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":"getTotalStakeBurn","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":"getTotalTitanXBurned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTreasuryPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUndistributedETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUndistributedTitanX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserBurnAmplifierBonus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserBurnTotal","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":"uint256","name":"mintPower","type":"uint256"},{"internalType":"uint16","name":"numOfDays","type":"uint16"},{"internalType":"uint256","name":"mintableHlx","type":"uint256"},{"internalType":"uint48","name":"mintStartTs","type":"uint48"},{"internalType":"uint48","name":"maturityTs","type":"uint48"},{"internalType":"uint256","name":"mintPowerBonus","type":"uint256"},{"internalType":"uint256","name":"EAABonus","type":"uint256"},{"internalType":"uint256","name":"mintedHlx","type":"uint256"},{"internalType":"uint256","name":"mintCost","type":"uint256"},{"internalType":"uint256","name":"penalty","type":"uint256"},{"internalType":"uint256","name":"titanBurned","type":"uint256"},{"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":"hRank","type":"uint256"},{"internalType":"uint256","name":"gMintPower","type":"uint256"},{"components":[{"internalType":"uint256","name":"mintPower","type":"uint256"},{"internalType":"uint16","name":"numOfDays","type":"uint16"},{"internalType":"uint256","name":"mintableHlx","type":"uint256"},{"internalType":"uint48","name":"mintStartTs","type":"uint48"},{"internalType":"uint48","name":"maturityTs","type":"uint48"},{"internalType":"uint256","name":"mintPowerBonus","type":"uint256"},{"internalType":"uint256","name":"EAABonus","type":"uint256"},{"internalType":"uint256","name":"mintedHlx","type":"uint256"},{"internalType":"uint256","name":"mintCost","type":"uint256"},{"internalType":"uint256","name":"penalty","type":"uint256"},{"internalType":"uint256","name":"titanBurned","type":"uint256"},{"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":"uint256","name":"hlxAmount","type":"uint256"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint16","name":"numOfDays","type":"uint16"},{"internalType":"uint48","name":"stakeStartTs","type":"uint48"},{"internalType":"uint48","name":"maturityTs","type":"uint48"},{"internalType":"uint256","name":"titanBurned","type":"uint256"},{"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":"uint256","name":"hlxAmount","type":"uint256"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint16","name":"numOfDays","type":"uint16"},{"internalType":"uint48","name":"stakeStartTs","type":"uint48"},{"internalType":"uint48","name":"maturityTs","type":"uint48"},{"internalType":"uint256","name":"titanBurned","type":"uint256"},{"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":"user","type":"address"}],"name":"getUserTitanXClaimableTotal","outputs":[{"internalType":"uint256","name":"reward","type":"uint256"}],"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":[{"internalType":"address","name":"contractAddress","type":"address"}],"name":"isWhiteListed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"manualDailyUpdate","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":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"onBurn","outputs":[],"stateMutability":"nonpayable","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":"newAddress","type":"address"}],"name":"setNewInvestmentAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"contractAddress","type":"address"}],"name":"setTitanXContractAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"contractAddress","type":"address"}],"name":"setTreasuryContractAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"mintPower","type":"uint256"},{"internalType":"uint256","name":"numOfDays","type":"uint256"},{"internalType":"uint256","name":"titanToBurn","type":"uint256"}],"name":"startMint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"numOfDays","type":"uint256"},{"internalType":"uint256","name":"titanToBurn","type":"uint256"}],"name":"startStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","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"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"userBurnMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"userBurnStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"userBurnTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"bool","name":"permit","type":"bool"}],"name":"whiteList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60a06040523480156200001157600080fd5b50604051620061a9380380620061a9833981016040819052620000349162000366565b6040518060400160405280600681526020016548454c494f5360d01b8152506040518060400160405280600381526020016209098b60eb1b815250816003908051906020019062000087929190620002a3565b5080516200009d906004906020840190620002a3565b50506001600581905542608052600655506c054d17db76321263eca00000006008556b0d92289838d21a99680000006009556816c4abbebea0100000600755620000ed62989680612710620003d6565b600a9081556200010290620f424090620003d6565b600b55601660208190527f6891b01528b646d3d0e273b7a56a751ccd9937f93c4f054a2a00404bb4fe64625560457f2320e82a07edbcb031d1b2e6236451ad287a5f528e6a2589351912cfedc3c5a3556101a460008190527f7e7449794f742b661d735866176c1d5a6b7b35ed500c377d57d70547609fa0ab556007600c55611770600d556103e8600e55603080546001600160a01b031916331790556001600160a01b038516620001c7576040516329d6af1160e01b815260040160405180910390fd5b6001600160a01b038416620001ef576040516329d6af1160e01b815260040160405180910390fd5b6001600160a01b03831662000217576040516329d6af1160e01b815260040160405180910390fd5b6001600160a01b0382166200023f576040516329d6af1160e01b815260040160405180910390fd5b603180546001600160a01b03199081166001600160a01b039788161790915560328054821692871692909217909155603380548216948616949094179093556035805484169285169290921790915560348054909216921691909117905562000441565b828054620002b19062000404565b90600052602060002090601f016020900481019282620002d5576000855562000320565b82601f10620002f057805160ff191683800117855562000320565b8280016001018555821562000320579182015b828111156200032057825182559160200191906001019062000303565b506200032e92915062000332565b5090565b5b808211156200032e576000815560010162000333565b80516001600160a01b03811681146200036157600080fd5b919050565b600080600080600060a086880312156200037f57600080fd5b6200038a8662000349565b94506200039a6020870162000349565b9350620003aa6040870162000349565b9250620003ba6060870162000349565b9150620003ca6080870162000349565b90509295509295909350565b6000816000190483118215151615620003ff57634e487b7160e01b600052601160045260246000fd5b500290565b600181811c908216806200041957607f821691505b602082108114156200043b57634e487b7160e01b600052602260045260246000fd5b50919050565b608051615d45620004646000396000818161108a0152612bcd0152615d456000f3fe6080604052600436106104ae5760003560e01c8062281d14146104d8578062ae5faa146104fc57806301ffc9a71461052957806306fdde0314610559578063095ea7b31461057b5780630cbe28d61461059b57806310cc8ee7146105bb57806312065fe0146105db5780631371bb40146105ee57806313aad5101461063457806318160ddd146106495780631ae409c01461065e578063216630b4146106735780632277d1bd14610688578063236393851461069d57806323b5675f146106b257806323b872dd146106c757806324f33a5b146106e7578063276b5c1a146106fc57806329b70d7a146107115780632d02347a146107475780632f77195114610774578063300284f214610789578063313ce567146107a957806333f3fd78146107c557806337c4f8c4146107e5578063392e353d14610805578063395093511461083a578063398015271461085a5780633a9693e11461087a5780633c34267f1461089a5780633d05012d146108ba578063403b285b146108cf57806341d6831a146108f657806345a1b92c1461090b578063462a8c2f1461092b5780634cf2d48f146109585780634df833af14610978578063510f8b9c1461098d578063544a6c59146109ad57806354f5d028146109f3578063566d0be314610a085780635742744114610a285780635a3c8b7d14610a485780635c1c5cb314610a5d5780635c3ef13014610a725780636272683314610ab8578063682d2d1114610acd578063692b6fdc14610aed5780636c52876b14610b1a5780636f60963314610b2f5780636f9170f614610b4457806370a0823114610b7d57806370c9b00214610b9d578063715018a614610bbd5780637629052d14610bd25780637789281e14610bf257806377a5426914610c075780637b763a2c14610c275780637d6b325314610c475780637ec26dca14610c67578063800bb26914610c7c578063842e298114610c91578063856a73da14610cbe578063880a083614610cde57806389de416514610cf35780638b13cdae14610d1357806392c1df5414610d335780639332812414610d485780639474782014610d6857806395d89b4114610d7d57806396d9720814610d925780639a5a6cd914610db25780639ed9922014610dd2578063a2573e5b14610df2578063a457c2d714610e10578063a76222af14610e30578063a9059cbb14610e45578063af4fb76314610e65578063af835b8a14610e7a578063b0977f6914610ea7578063b0aebfbc14610ebc578063b8fac78914610edc578063b984c94614610ef1578063baf20eef14610f06578063bb88603c14610f26578063c081f4c014610f3b578063c50312ad14610f50578063d16baeb914610f86578063d6f8688f14610fa6578063d819e19814610fe6578063d9af94af14611013578063dd62ed3e14611026578063dff96e9a14611046578063e33a3c941461105b578063e3af6d0a1461107b578063e3d3227d146110ae578063e8052174146110c3578063efe17023146110e3578063f2fde38b14611103578063f63ec50e14611123578063f80b0cfb14611138578063faa94d3b1461114d578063fac940f41461116d578063fbf9529d14611180578063fe9497a614611195578063ffb75cab146111b557600080fd5b366104d35734156104d15734603760008282546104cb919061561f565b90915550505b005b600080fd5b3480156104e457600080fd5b50600b545b6040519081526020015b60405180910390f35b34801561050857600080fd5b5061051c610517366004615653565b6111e2565b6040516104f39190615704565b34801561053557600080fd5b50610549610544366004615712565b6112b0565b60405190151581526020016104f3565b34801561056557600080fd5b5061056e6112e6565b6040516104f3919061573c565b34801561058757600080fd5b50610549610596366004615653565b611378565b3480156105a757600080fd5b506104d16105b6366004615791565b611390565b3480156105c757600080fd5b506104d16105d63660046157aa565b6113d9565b3480156105e757600080fd5b50476104e9565b3480156105fa57600080fd5b506104e96106093660046157d6565b6001600160a01b039182166000908152602f6020908152604080832093909416825291909152205490565b34801561064057600080fd5b506104d161154e565b34801561065557600080fd5b506002546104e9565b34801561066a57600080fd5b506006546104e9565b34801561067f57600080fd5b50601a546104e9565b34801561069457600080fd5b506104d1611558565b3480156106a957600080fd5b506104e961172d565b3480156106be57600080fd5b50600d546104e9565b3480156106d357600080fd5b506105496106e2366004615809565b611751565b3480156106f357600080fd5b50600e546104e9565b34801561070857600080fd5b506026546104e9565b34801561071d57600080fd5b506104e961072c366004615845565b6001600160a01b03166000908152602e602052604090205490565b34801561075357600080fd5b506104e9610762366004615791565b60009081526016602052604090205490565b34801561078057600080fd5b506019546104e9565b34801561079557600080fd5b506105496107a4366004615653565b611777565b3480156107b557600080fd5b50604051601281526020016104f3565b3480156107d157600080fd5b506104d16107e0366004615860565b6117f8565b3480156107f157600080fd5b506104d1610800366004615791565b611826565b34801561081157600080fd5b506108256108203660046158b0565b61184f565b604080519283526020830191909152016104f3565b34801561084657600080fd5b50610549610855366004615653565b611873565b34801561086657600080fd5b506104e96108753660046158d2565b611895565b34801561088657600080fd5b506104d1610895366004615845565b6118e7565b3480156108a657600080fd5b506104d16108b5366004615791565b611964565b3480156108c657600080fd5b506037546104e9565b3480156108db57600080fd5b506032546001600160a01b03165b6040516104f39190615911565b34801561090257600080fd5b506036546104e9565b34801561091757600080fd5b506104d1610926366004615653565b6119d3565b34801561093757600080fd5b506104e9610946366004615791565b60009081526010602052604090205490565b34801561096457600080fd5b506104d1610973366004615845565b611a36565b34801561098457600080fd5b506038546104e9565b34801561099957600080fd5b506104e96109a8366004615845565b611a87565b3480156109b957600080fd5b506104e96109c83660046157d6565b6001600160a01b039182166000908152603a6020908152604080832093909416825291909152205490565b3480156109ff57600080fd5b50602c546104e9565b348015610a1457600080fd5b506104d1610a23366004615791565b611aef565b348015610a3457600080fd5b50610825610a433660046158b0565b611b2a565b348015610a5457600080fd5b506009546104e9565b348015610a6957600080fd5b506104d1611b6d565b348015610a7e57600080fd5b506104e9610a8d3660046157d6565b6001600160a01b039182166000908152603c6020908152604080832093909416825291909152205490565b348015610ac457600080fd5b506104d1611bad565b348015610ad957600080fd5b506104e9610ae8366004615653565b611cc1565b348015610af957600080fd5b506104e9610b08366004615791565b60009081526011602052604090205490565b348015610b2657600080fd5b506104e9611e2a565b348015610b3b57600080fd5b506008546104e9565b348015610b5057600080fd5b50610549610b5f366004615845565b6001600160a01b03166000908152603b602052604090205460ff1690565b348015610b8957600080fd5b506104e9610b98366004615845565b611e42565b348015610ba957600080fd5b50610825610bb83660046158b0565b611e5d565b348015610bc957600080fd5b506104d1611e81565b348015610bde57600080fd5b506104d1610bed366004615845565b611e93565b348015610bfe57600080fd5b506022546104e9565b348015610c1357600080fd5b506104d1610c22366004615860565b611ee4565b348015610c3357600080fd5b506104d1610c42366004615653565b611f01565b348015610c5357600080fd5b506104d1610c62366004615845565b611f29565b348015610c7357600080fd5b506017546104e9565b348015610c8857600080fd5b506104e9611f7a565b348015610c9d57600080fd5b50610cb1610cac366004615845565b611f92565b6040516104f39190615925565b348015610cca57600080fd5b50610549610cd9366004615653565b612091565b348015610cea57600080fd5b506021546104e9565b348015610cff57600080fd5b50610825610d0e366004615653565b612112565b348015610d1f57600080fd5b506104e9610d2e366004615791565b612142565b348015610d3f57600080fd5b50601c546104e9565b348015610d5457600080fd5b50600f5460ff166040516104f39190615989565b348015610d7457600080fd5b506104e961214d565b348015610d8957600080fd5b5061056e612158565b348015610d9e57600080fd5b506104d1610dad3660046159a3565b612167565b348015610dbe57600080fd5b506104e9610dcd366004615845565b612194565b348015610dde57600080fd5b506104d1610ded3660046159a3565b6121cf565b348015610dfe57600080fd5b506031546001600160a01b03166108e9565b348015610e1c57600080fd5b50610549610e2b366004615653565b6121ec565b348015610e3c57600080fd5b506104e9612272565b348015610e5157600080fd5b50610549610e60366004615653565b6122e4565b348015610e7157600080fd5b506104e96122f2565b348015610e8657600080fd5b506104e9610e95366004615791565b60009081526012602052604090205490565b348015610eb357600080fd5b506023546104e9565b348015610ec857600080fd5b506104d1610ed7366004615845565b612304565b348015610ee857600080fd5b506025546104e9565b348015610efd57600080fd5b506020546104e9565b348015610f1257600080fd5b506104d1610f21366004615791565b612381565b348015610f3257600080fd5b506104d16123a5565b348015610f4757600080fd5b506024546104e9565b348015610f5c57600080fd5b506104e9610f6b366004615845565b6001600160a01b03166000908152601d602052604090205490565b348015610f9257600080fd5b506104d1610fa13660046159ea565b6123ff565b348015610fb257600080fd5b50610fc6610fc1366004615653565b612459565b6040805194855260208501939093529183015260608201526080016104f3565b348015610ff257600080fd5b50611006611001366004615653565b61257a565b6040516104f39190615adb565b34801561101f57600080fd5b50426104e9565b34801561103257600080fd5b506104e96110413660046157d6565b61265f565b34801561105257600080fd5b50601b546104e9565b34801561106757600080fd5b506104e9611076366004615845565b61268a565b34801561108757600080fd5b507f00000000000000000000000000000000000000000000000000000000000000006104e9565b3480156110ba57600080fd5b506104d16126e9565b3480156110cf57600080fd5b506104e96110de366004615845565b612783565b3480156110ef57600080fd5b506104d16110fe366004615653565b61279e565b34801561110f57600080fd5b506104d161111e366004615845565b6127d0565b34801561112f57600080fd5b50600a546104e9565b34801561114457600080fd5b506018546104e9565b34801561115957600080fd5b506104e9611168366004615845565b6127e1565b6104d161117b3660046157aa565b6127fc565b34801561118c57600080fd5b506007546104e9565b3480156111a157600080fd5b506104e96111b0366004615845565b61292b565b3480156111c157600080fd5b506111d56111d0366004615845565b61298a565b6040516104f39190615aea565b6111ea615503565b6001600160a01b038316600090815260286020908152604080832085845282528083205483526029825291829020825160e0810184528154815260018201549281019290925260028082015461ffff81169484019490945265ffffffffffff62010000850481166060850152600160401b9094049093166080830152600381015460a083015260048101549192909160c084019160ff909116908111156112935761129361567d565b60028111156112a4576112a461567d565b90525090505b92915050565b60006001600160e01b031982166301ffc9a760e01b14806112aa57506001600160e01b031982166311686e4b60e21b1492915050565b6060600380546112f590615b4e565b80601f016020809104026020016040519081016040528092919081815260200182805461132190615b4e565b801561136e5780601f106113435761010080835404028352916020019161136e565b820191906000526020600020905b81548152906001019060200180831161135157829003601f168201915b5050505050905090565b600033611386818585612a9b565b5060019392505050565b611398612bbf565b6113a0612e0d565b6113cc336113c733846113b260065490565b600160036113c2600f5460ff1690565b612e67565b6130c9565b6113d66001600555565b50565b6113e1612bbf565b6113e9612e0d565b826113f333611e42565b101561141257604051631d78718160e01b815260040160405180910390fd5b33321461147957336000908152603b602052604090205460ff166114795760405162461bcd60e51b815260206004820152601960248201527821b7b73a3930b1ba103737ba103bb434ba32b634b9ba32b21760391b60448201526064015b60405180910390fd5b80156114885761148881613176565b6114923384613207565b61153f3361153a3386866114a560075490565b600654600f5460ff168960008b116114be576000613327565b603360009054906101000a90046001600160a01b03166001600160a01b031663665f8efb6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611511573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115359190615b83565b613327565b61345e565b6115496001600555565b505050565b611556612bbf565b565b611560612bbf565b611568612e0d565b600061157360225490565b6021546115809190615b9c565b905060018110156115a45760405163df8afe1960e01b815260040160405180910390fd5b6000806000806036546000146115c8576115bc61359b565b92965090945090925090505b60006115d360065490565b9050600060016115e5601689856137a9565b60018111156115f6576115f661567d565b148015611614575060008160018111156116125761161261567d565b145b61161e5780611623565b506001805b506001611632604589856137a9565b60018111156116435761164361567d565b1480156116615750600081600181111561165f5761165f61567d565b145b61166b5780611670565b506001805b5060016116806101a489856137a9565b60018111156116915761169161567d565b1480156116af575060008160018111156116ad576116ad61567d565b145b6116b957806116be565b506001805b5060018160018111156116d3576116d361567d565b141561170a576000600f5460ff1660018111156116f2576116f261567d565b141561170a5761170a600f805460ff19166001179055565b851561171c5761171c8686858761384f565b505050505050506115566001600555565b60006019546018546017546117429190615b9c565b61174c9190615b9c565b905090565b60003361175f85828561389a565b61176a85858561390e565b60019150505b9392505050565b60006001600160a01b0383166117a0576040516329d6af1160e01b815260040160405180910390fd5b336000818152603a602090815260408083206001600160a01b038816808552925280832086905551859391927ff8e109bcddf5e12132b7cd8a8517d97498f50c7ac595874d6f513243098b079891a450600192915050565b611800612bbf565b611808612e0d565b6118158585858585613aa0565b61181f6001600555565b5050505050565b61182e612bbf565b611836612e0d565b6113cc33600061184833856001613b15565b6001613d13565b60009182526014602090815260408084209284529190529020600181015490549091565b600033611386818585611886838361265f565b611890919061561f565b612a9b565b6000806118aa866118a560085490565b613e25565b905060006118b88583613e35565b90506118da87876118c860095490565b600b546118d489611a87565b86613e4f565b925050505b949350505050565b6031546001600160a01b0316336001600160a01b03161461191b576040516339e2f41f60e21b815260040160405180910390fd5b6001600160a01b038116611942576040516329d6af1160e01b815260040160405180910390fd5b603180546001600160a01b0319166001600160a01b0392909216919091179055565b61196c612bbf565b611974612e0d565b8061199257604051630aa71b7760e41b815260040160405180910390fd5b8061199c33611e42565b10156119bb57604051631d78718160e01b815260040160405180910390fd5b6119c53382613207565b6113cc336000836000613d13565b6035546001600160a01b03163314611a1b5760405162461bcd60e51b815260206004820152600b60248201526a09edcd8f240a8d2e8c2dcb60ab1b6044820152606401611470565b8060386000828254611a2d919061561f565b90915550505050565b611a3e613f7e565b6001600160a01b038116611a65576040516329d6af1160e01b815260040160405180910390fd5b603480546001600160a01b0319166001600160a01b0392909216919091179055565b600080611a9383612783565b905080611aa35750600092915050565b680204fce5e3e2502611601f1b8110611ac65750676f05b59d3b20000092915050565b680204fce5e3e2502611601f1b611ae582676f05b59d3b200000615bb3565b6117709190615bd2565b611af7612bbf565b611aff612e0d565b6113cc336000611b233385611b1360065490565b600260036113c2600f5460ff1690565b6002613d13565b6000806000611b3860075490565b9050611b45858583613fa9565b9250611b59670de0b6b3a764000082615bd2565b611b639086615bd2565b9150509250929050565b611b75612bbf565b611b7d612e0d565b600080600080611b8b61359b565b9350935093509350611b9f8484848461384f565b505050506115566001600555565b611bb5612bbf565b611bbd612e0d565b600080600080611bcd6016614000565b9092509050611bdc828561561f565b9350611be8818461561f565b9250611bf46045614000565b9092509050611c03828561561f565b9350611c0f818461561f565b9250611c1c6101a4614000565b9092509050611c2b828561561f565b9350611c37818461561f565b925083158015611c45575082155b15611c63576040516354d2b34960e01b815260040160405180910390fd5b8315611c7457611c74335b85614037565b8215611c8457611c8433846140d3565b6040518390859033907ff01da32686223933d8a18a391060918c7f11a3648639edd87ae013e2e273174390600090a4505050506115566001600555565b6001600160a01b0382166000908152601e602090815260408083208484529091528120805460019091015481611d0a57604051637a2e11eb60e01b815260040160405180910390fd5b6000828152601f6020908152604080832081516101808101835281548152600182015461ffff169381019390935260028082015492840192909252600381015465ffffffffffff8082166060860152600160301b909104166080840152600481015460a0840152600581015460c0840152600681015460e0840152600781015461010084015260088101546101208401526009810154610140840152600a810154909161016084019160ff1690811115611dc657611dc661567d565b6002811115611dd757611dd761567d565b815250509050806040015193506000611dfc8260a00151836000015185601c5461416e565b9050611e0b6298968082615bd2565b611e15908661561f565b611e1f908661561f565b979650505050505050565b6000611e3560245490565b601b5461174c919061561f565b6001600160a01b031660009081526020819052604090205490565b60009182526013602090815260408084209284529190529020600181015490549091565b611e89613f7e565b61155660006141be565b611e9b613f7e565b6001600160a01b038116611ec2576040516329d6af1160e01b815260040160405180910390fd5b603580546001600160a01b0319166001600160a01b0392909216919091179055565b611eec612bbf565b611ef4612e0d565b61181585858585856141e0565b611f09612bbf565b611f11612e0d565b611f1b828261420f565b611f256001600555565b5050565b611f31613f7e565b6001600160a01b038116611f58576040516329d6af1160e01b815260040160405180910390fd5b603380546001600160a01b0319166001600160a01b0392909216919091179055565b6000611f8560255490565b60205461174c9190615b9c565b6001600160a01b038116600090815260276020526040812054606091816001600160401b03811115611fc657611fc6615bf4565b604051908082528060200260200182016040528015611fff57816020015b611fec615540565b815260200190600190039081611fe45790505b50905060015b82811161208957604080516060810182528281526001600160a01b038716600090815260286020908152838220858352815290839020549082015290810161204d87846111e2565b90528261205b600184615b9c565b8151811061206b5761206b615c0a565b6020026020010181905250808061208190615c20565b915050612005565b509392505050565b60006001600160a01b0383166120ba576040516329d6af1160e01b815260040160405180910390fd5b336000818152603c602090815260408083206001600160a01b038816808552925280832086905551859391927fd508e6bf29a4128e58df993e4fe1db1d926db54e85247bc919df2c52eb78212591a450600192915050565b6001600160a01b039190911660009081526015602090815260408083209383529290522080546001909101549091565b60006112aa8261426d565b600061174c30611e42565b6060600480546112f590615b4e565b61216f612bbf565b612177612e0d565b61218484848484336141e0565b61218e6001600555565b50505050565b6001600160a01b0381166000908152602b60205260408120816121b6846127e1565b8152602001908152602001600020600101549050919050565b6121d7612bbf565b6121df612e0d565b6121848484848433613aa0565b600033816121fa828661265f565b90508381101561225a5760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401611470565b6122678286868403612a9b565b506001949350505050565b6035546040516370a0823160e01b81526000916001600160a01b0316906370a08231906122a3903090600401615911565b602060405180830381865afa1580156122c0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061174c9190615b83565b60003361138681858561390e565b600060225460215461174c9190615b9c565b6032546001600160a01b0316336001600160a01b031614612338576040516339e2f41f60e21b815260040160405180910390fd5b6001600160a01b03811661235f576040516329d6af1160e01b815260040160405180910390fd5b603280546001600160a01b0319166001600160a01b0392909216919091179055565b612389612bbf565b612391612e0d565b6113cc6123a033836000613b15565b6142ce565b6123ad612bbf565b6033546001600160a01b0316336001600160a01b0316146123e157604051630eb3f67960e21b815260040160405180910390fd5b603354611556906001600160a01b03166123fa81611e42565b613207565b612407613f7e565b6001600160a01b03821661242e576040516329d6af1160e01b815260040160405180910390fd5b6001600160a01b03919091166000908152603b60205260409020805460ff1916911515919091179055565b60008060008060006124778660009081526012602052604090205490565b90506124838787612112565b90935091506000612493886127e1565b9050835b82811161256e576000806124ab8a84611e5d565b9150915060006124bb8b8561184f565b50905060006124cc8d8988866142f6565b9850905083158015906124de57508015155b1561250d57670de0b6b3a76400006124f68583615bb3565b6125009190615bd2565b61250a908c61561f565b9a505b811580159061251b57508015155b1561254a57670de0b6b3a76400006125338383615bb3565b61253d9190615bd2565b612547908b61561f565b99505b61255585600161561f565b985050505050808061256690615c20565b915050612497565b50505092959194509250565b612582615561565b6001600160a01b0383166000908152601e602090815260408083208584528252808320548352601f82529182902082516101808101845281548152600182015461ffff169281019290925260028082015493830193909352600381015465ffffffffffff8082166060850152600160301b909104166080830152600481015460a0830152600581015460c0830152600681015460e0830152600781015461010083015260088101546101208301526009810154610140830152600a8101549192909161016084019160ff909116908111156112935761129361567d565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b600080612698836016612459565b509092506126aa91508290508361561f565b91506126b7836045612459565b509092506126c991508290508361561f565b91506126d7836101a4612459565b5090925061177091508290508361561f565b6033546001600160a01b0316336001600160a01b03161461271d576040516339e2f41f60e21b815260040160405180910390fd5b600160395460ff1660018111156127365761273661567d565b141561275557604051633ee45dcb60e21b815260040160405180910390fd5b6039805460ff19166001179055603354611556906001600160a01b03166714adf4b7320334b9601e1b6130c9565b6001600160a01b03166000908152602d602052604090205490565b6127a6612bbf565b6127ae612e0d565b611f1b826113c784846127c060065490565b600160046113c2600f5460ff1690565b6127d8613f7e565b6113d6816141be565b6001600160a01b03166000908152602a602052604090205490565b612804612e0d565b61280c612bbf565b6103e861281833610f6b565b61282390600161561f565b1115612842576040516334da899760e11b815260040160405180910390fd5b80156128515761285181613176565b60006040518060c001604052808581526020018481526020018381526020018561287a601c5490565b612884919061561f565b815260200161289260175490565b61289d90600161561f565b81526020016128af866118a560085490565b9052905060006128eb3383516020850151600954600a54600b546128d233611a87565b89606001518a608001518b60a001518c6040015161437f565b601a546128f8919061561f565b90506129168260800151836060015183601792909255601c55601a55565b61291f85614420565b50506115496001600555565b600080612939836016612459565b5091925061294b91508290508361561f565b9150612958836045612459565b5091925061296a91508290508361561f565b9150612978836101a4612459565b5091925061177091508290508361561f565b6001600160a01b0381166000908152601d6020526040902054606090806001600160401b038111156129be576129be615bf4565b6040519080825280602002602001820160405280156129f757816020015b6129e46155e1565b8152602001906001900390816129dc5790505b50915060015b818111612a9457604080516080810182528281526001600160a01b0386166000908152601e6020908152838220858352808252848320805483860152928690529052600101549181019190915260608101612a58868461257a565b905283612a66600184615b9c565b81518110612a7657612a76615c0a565b60200260200101819052508080612a8c90615c20565b9150506129fd565b5050919050565b6001600160a01b038316612afd5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401611470565b6001600160a01b038216612b5e5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401611470565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b600654600062015180612bf27f000000000000000000000000000000000000000000000000000000000000000042615b9c565b612bfc9190615bd2565b612c0790600161561f565b905081811115611f2557600854600954600a54600b546000612c298787615b9c565b905060005b81811015612de957612710612c4561271a88615bb3565b612c4f9190615bd2565b9550612710612c606126ed87615bb3565b612c6a9190615bd2565b9450612710612c7b6126ed86615bb3565b612c859190615bd2565b935068327cb2734119d3b7a9601f1b861115612cab5768327cb2734119d3b7a9601f1b95505b600c548810158015612cbe5750600d5415155b8015612cce5750611b58600e5414155b15612d20576032600d6000828254612ce69190615b9c565b925050819055506032600e6000828254612d00919061561f565b925050819055506007600c6000828254612d1a919061561f565b90915550505b6958f03ee118a13e800000851015612d40576958f03ee118a13e80000094505b612d4e6103e8612710615bb3565b841015612d6657612d636103e8612710615bb3565b93505b60458711612d8257612d7b6202361f84615b9c565b9250612d87565b600092505b85612d9189615c20565b604080518881526020810188905290810186905290995089907f1b4cf4ae62850d5107d63ca4895b7d62305df42148813646f7f4a76638a9618a9060600160405180910390a380612de181615c20565b915050612c2e565b5050600893909355600991909155600a55600b5560065550600f805460ff19169055565b60026005541415612e605760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401611470565b6002600555565b6001600160a01b038616600090815260286020908152604080832088845290915281205480612ea95760405163de9d71e560e01b815260040160405180910390fd5b6000818152602960209081526040808320815160e0810183528154815260018201549381019390935260028082015461ffff81169385019390935265ffffffffffff62010000840481166060860152600160401b9093049092166080840152600381015460a08401526004810154909160c084019160ff1690811115612f3157612f3161567d565b6002811115612f4257612f4261567d565b905250905060018160c001516002811115612f5f57612f5f61567d565b1415612f7e5760405163081b599f60e41b815260040160405180910390fd5b60028160c001516002811115612f9657612f9661567d565b1415612fb55760405163c3fb2f4760e01b815260040160405180910390fd5b6004856004811115612fc957612fc961567d565b148015612fe15750806080015165ffffffffffff1642105b15612fff57604051639d2f92f560e01b815260040160405180910390fd5b60208101518151613016908b9083908b898c614508565b50600187600481111561302b5761302b61567d565b14156130655760256000815461304090615c20565b909155506000838152602960205260409020600401805460ff191660011790556130af565b60028760048111156130795761307961567d565b14156130af5760266000815461308e90615c20565b909155506000838152602960205260409020600401805460ff191660021790555b6130bb8a84848a6146b3565b9a9950505050505050505050565b6001600160a01b03821661311f5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401611470565b8060026000828254613131919061561f565b90915550506001600160a01b03821660008181526020818152604080832080548601905551848152600080516020615cf0833981519152910160405180910390a35050565b73f19308f923582a6f7c465e5ce7a9dc1bec6665b16333f3fd783360315460405160e084901b6001600160e01b03191681526001600160a01b03928316600480830191909152602482018790526044820181905260648201529116608482015260a401600060405180830381600087803b1580156131f357600080fd5b505af115801561181f573d6000803e3d6000fd5b6001600160a01b0382166132675760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401611470565b6001600160a01b038216600090815260208190526040902054818110156132db5760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401611470565b6001600160a01b038316600081815260208181526040808320868603905560028054879003905551858152919291600080516020615cf0833981519152910160405180910390a3505050565b6001600160a01b03881660009081526027602052604081208054829190829061334f90615c20565b918290555090506103e88111156133795760405163174a8f9960e21b815260040160405180910390fd5b601e881080613389575061033e88115b156133a75760405163d1b1ca9d60e01b815260040160405180910390fd5b60006133b48a8a8a613fa9565b905060016133ca670de0b6b3a764000083615bd2565b10156133e95760405163082aaa8760e41b815260040160405180910390fd5b84156134325760006133fc86868d614775565b90506107d081111561340d57506107d05b61271061341a8284615bb3565b6134249190615bd2565b61342e908361561f565b9150505b613440828c8c8c858a6147ba565b61344f8b828c8a8a6000614508565b9b9a5050505050505050505050565b8060011415611f255760166000526012602052600080516020615cd08339815191525415611f255760166000526012602052600080516020615cd0833981519152546134ab90600161561f565b6001600160a01b038316600090815260156020908152604080832060168452825282209290925560459052601290527f2438ba9a9fcd62a1364c4e295291b48be211763142dce8144a28ac91969f666f5461350790600161561f565b6001600160a01b03831660009081526015602090815260408083206045845282528220929092556101a49052601290527fb7ff304c5ab3d0f93c4868e4aee66ebe077d23da2da94c4b2280ba2f567bd0225461356490600161561f565b6001600160a01b03831660009081526015602090815260408083206101a4845290915290206001600160601b039190911690555050565b6000806000806000603654905080600014156135ca57604051630b0daf9d60e21b815260040160405180910390fd5b60006036819055604051829133917f97934fea6c25f21bceb486fba06c291987ec0bc4e293ea7b328285bdb07a180f9190a3620f424061360c610bb883615bb3565b6136169190615bd2565b94506136228582615b9c565b9050612710613630600d5490565b61363a9083615bb3565b6136449190615bd2565b9350612710613652600e5490565b61365c9083615bb3565b6136669190615bd2565b925061271061367660c883615bb3565b6136809190615bd2565b9150600082846136908785615b9c565b61369a9190615b9c565b6136a49190615b9c565b9050801561371f5760006127106136bd610dac84615bb3565b6136c79190615bd2565b905060006127106136da610bb885615bb3565b6136e49190615bd2565b90506136f1601683614971565b6136fc604582614971565b61371c6101a48261370d8587615b9c565b6137179190615b9c565b614971565b50505b60375480156137a0576000603781905561271061373e610dac84615bb3565b6137489190615bd2565b9050600061271061375b610bb885615bb3565b6137659190615bd2565b905061377260168361498f565b61377d60458261498f565b61379d6101a48261378e8587615b9c565b6137989190615b9c565b61498f565b50505b50505090919293565b6000838152601660205260408120548210156137c757506000611770565b6137d0846149ad565b600084815260106020908152604080832054601190925290912054811580156137f7575080155b1561380757600092505050611770565b61381386838388614a18565b506040518290879033907f973833b060ae272a085ff5b17b764e39ddac6a5612bdc9337f586dc7efee604990600090a450600195945050505050565b61385833611c6e565b60315461386e906001600160a01b031682614037565b603354613884906001600160a01b031684614037565b60345461218e906001600160a01b031683614037565b60006138a6848461265f565b9050600019811461218e57818110156139015760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401611470565b61218e8484848403612a9b565b6001600160a01b0383166139725760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401611470565b6001600160a01b0382166139d45760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401611470565b6001600160a01b03831660009081526020819052604090205481811015613a4c5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401611470565b6001600160a01b0384811660008181526020818152604080832087870390559387168083529184902080548701905592518581529092600080516020615cf0833981519152910160405180910390a361218e565b83613abe57604051630aa71b7760e41b815260040160405180910390fd5b83613ac886611e42565b1015613ae757604051631d78718160e01b815260040160405180910390fd5b613af285338661389a565b613afc8383614ae1565b613b068585613207565b61181f85858585856000614c01565b6001600160a01b0383166000908152601e602090815260408083208584529091528120805460019091015481613b5e57604051637a2e11eb60e01b815260040160405180910390fd5b6000828152601f6020908152604080832081516101808101835281548152600182015461ffff169381019390935260028082015492840192909252600381015465ffffffffffff8082166060860152600160301b909104166080840152600481015460a0840152600581015460c0840152600681015460e0840152600781015461010084015260088101546101208401526009810154610140840152600a810154909161016084019160ff1690811115613c1a57613c1a61567d565b6002811115613c2b57613c2b61567d565b905250905060018161016001516002811115613c4957613c4961567d565b1415613c68576040516339f4887960e21b815260040160405180910390fd5b60028161016001516002811115613c8157613c8161567d565b1415613ca0576040516323acf12560e01b815260040160405180910390fd5b42816080015165ffffffffffff16118015613ccc57506000856001811115613cca57613cca61567d565b145b15613cea57604051636971be5760e11b815260040160405180910390fd5b8060400151601a6000828254613d009190615b9c565b90915550611e1f90508784848489614cff565b6001600160a01b0384166000908152602d602052604081208054849290613d3b90849061561f565b9250508190555081602c6000828254613d54919061561f565b90915550506001600160a01b03831615613dd2576001600160a01b0383166000908152602e602052604081208054849290613d9090849061561f565b90915550506001600160a01b038084166000908152602f6020908152604080832093881683529290529081208054849290613dcc90849061561f565b90915550505b826001600160a01b0316846001600160a01b03167fdf82885dedaf240ee119dba88f242ab92b4bff124f1c029d077cad9a57fb97c68484604051613e17929190615c3b565b60405180910390a350505050565b6000620186a0611ae58484615bb3565b60008082613e4585612710615bb3565b6118df9190615bd2565b60008086613e5d8988615bb3565b613e679190615bb3565b905086600114613eab57612710613e7f600189615b9c565b613e8a600b84615bb3565b613e949190615bb3565b613e9e9190615bd2565b613ea89082615b9c565b90505b9050808415613ee557620f42406064613ec48784615bb3565b613ece9190615bd2565b613ed89190615bd2565b613ee2908361561f565b91505b8315613f2157670de0b6b3a76400006064613f008684615bb3565b613f0a9190615bd2565b613f149190615bd2565b613f1e908361561f565b91505b8215613f65576103e88311613f365782613f3a565b6103e85b92506000612710613f4b8585615bb3565b613f559190615bd2565b9050613f61818461561f565b9250505b613f72620186a083615bd2565b98975050505050505050565b6030546001600160a01b0316331461155657604051635e8a044f60e01b815260040160405180910390fd5b60008364174876e800613fbb8561426d565b613fc59083615bb3565b613fcf9190615bd2565b613fd9908261561f565b9050613fed670de0b6b3a764000084615bd2565b613ff79082615bd2565b95945050505050565b600080808080806140113388612459565b935093509350935061402b6140233390565b888484614f29565b50919590945092505050565b6001600160a01b03821661405e576040516329d6af1160e01b815260040160405180910390fd5b60355460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb906140909085908590600401615c58565b6020604051808303816000875af11580156140af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115499190615c71565b6001600160a01b0382166140fa576040516329d6af1160e01b815260040160405180910390fd5b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114614147576040519150601f19603f3d011682016040523d82523d6000602084013e61414c565b606091505b505090508061154957604051633ad7296360e11b815260040160405180910390fd5b600082821161417f575060006118df565b620186a0670de0b6b3a76400006141968585615b9c565b6141a08789615bb3565b6141aa9190615bb3565b6141b49190615bb3565b613ff79190615bd2565b603080546001600160a01b0319166001600160a01b0392909216919091179055565b6141e985614fd6565b6141f38383614ae1565b61181f856142058787611b1360065490565b8585856002614c01565b6142188261505b565b614223600080614ae1565b600061423183836001613b15565b60315490915061425d906001600160a01b031661271061425384610320615bb3565b6113c79190615bd2565b6115498382600080336001614c01565b6000601e82116142815750620f4240919050565b600061428e601e84615b9c565b9050600061429f601e61033e615b9c565b8211156142b8576142b3601e61033e615b9c565b6142ba565b815b9050610190613e4564174876e80083615bb3565b6142d833826130c9565b6032546113d6906001600160a01b0316612710614253846064615bb3565b600080845b848111614374576001600160a01b0387166000908152602b60209081526040808320848452909152902054841061435a576001600160a01b0387166000908152602b60209081526040808320848452909152902060010154925061435f565b614374565b9450848061436c81615c20565b9150506142fb565b509095939450505050565b600089158061438e575060fa8a115b156143ac576040516332c13ef760e01b815260040160405180910390fd5b8a15806143bb5750620186a08b115b156143d9576040516304c7176560e31b815260040160405180910390fd5b600082156143ee576143eb8385613e35565b90505b6143fc8c8c8c8b8b86613e4f565b91506144108d8d8d858d8d8b8d8c8c6150af565b509b9a5050505050505050505050565b600061442f826118a560085490565b6035549091506001600160a01b03166323b872dd336040516001600160e01b031960e084901b1681526001600160a01b039091166004820152306024820152604481018490526064016020604051808303816000875af1158015614497573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144bb9190615c71565b5080603660008282546144ce919061561f565b909155505060065460405182919033907fd833e83f161e4ddfb1306cdf11a374a0a23393f008f9394b85999b988c232e3690600090a45050565b6001600160a01b0386166000908152602a6020908152604080832054602b83528184208185529092528220600101548284600481111561454a5761454a61567d565b14156145d4578161455a57600192505b614564888261561f565b6001600160a01b038a166000908152602b602052604081209061458685615c20565b94508481526020019081526020016000206001018190555087602160008282546145b0919061561f565b9250508190555086602360008282546145c9919061561f565b909155506146499050565b6145de8882615b9c565b6001600160a01b038a166000908152602b602052604081209061460085615c20565b945084815260200190815260200160002060010181905550876022600082825461462a919061561f565b9250508190555086602360008282546146439190615b9c565b90915550505b600085600181111561465d5761465d61567d565b146146725761466d86600161561f565b614674565b855b6001600160a01b039099166000818152602b6020908152604080832086845282528083209c909c55918152602a90915298909820559695505050505050565b6000808360000151905060006146e3856060015165ffffffffffff16866080015165ffffffffffff1642876152d6565b9050600060646146f38385615bb3565b6146fd9190615bd2565b90506147098184615b9c565b9350806024600082825461471d919061561f565b90915550506040805185815260208101839052839189916001600160a01b038c16917f971d9ff3287b3ba75194105e7281e55c93b0a89cad9915664bb3fd9211f8d5f1910160405180910390a4505050949350505050565b600080670de0b6b3a764000061478b8585615bb3565b6147959190615bd2565b90506000816147a661271088615bb3565b6147b09190615bd2565b9695505050505050565b60006020600081546147cb90615c20565b9182905550905060006147e16201518086615bb3565b6147eb904261561f565b905060006040518060e001604052808881526020018681526020018761ffff1681526020014265ffffffffffff1681526020018365ffffffffffff168152602001858152602001600060028111156148455761484561567d565b90526001600160a01b03891660009081526028602090815260408083208d845282528083208790558683526029825291829020835181559083015160018083019190915591830151600280830180546060870151608088015161ffff9095166001600160401b0319909216919091176201000065ffffffffffff928316021765ffffffffffff60401b1916600160401b91909416029290921790915560a0840151600383015560c0840151600483018054959650869593949193909260ff199091169190849081111561491a5761491a61567d565b021790555090505082886001600160a01b03167f04e1761ddc1451be2b9abb20d50c7e317778879612685874154566bbb4acafc7888460405161495e929190615c8e565b60405180910390a3505050505050505050565b60008281526010602052604081208054839290611a2d90849061561f565b60008281526011602052604081208054839290611a2d90849061561f565b60008181526016602052604090205460065481811061154957826149d18383615b9c565b6149db9190615bd2565b6149e690600161561f565b6149f09084615bb3565b60008481526016602052604081208054909190614a0e90849061561f565b9091555050505050565b6000848152601060209081526040808320839055601182528083208390556012909152812080548290614a4a90615c20565b9182905550905081614a64670de0b6b3a764000086615bb3565b614a6e9190615bd2565b600086815260136020908152604080832085845290915290206001810191909155600654905581614aa7670de0b6b3a764000085615bb3565b614ab19190615bd2565b60009586526014602090815260408088208489529091529095206001810195909555600654909455509192915050565b6008614aed838361561f565b1115614b0c57604051632902579f60e01b815260040160405180910390fd5b6040516301ffc9a760e01b80825233916301ffc9a791614b2e91600401615ca3565b602060405180830381865afa158015614b4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614b6f9190615c71565b1580614be357506040516301ffc9a760e01b815233906301ffc9a790614ba0906311686e4b60e21b90600401615ca3565b602060405180830381865afa158015614bbd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614be19190615c71565b155b15611f255760405163d03681f360e01b815260040160405180910390fd5b614c0d86338784613d13565b6000808415614c4757614c236127106064615bb3565b612710614c30878a615bb3565b614c3a9190615bb3565b614c449190615bd2565b91505b8515614c7e57614c5a6127106064615bb3565b612710614c67888a615bb3565b614c719190615bb3565b614c7b9190615bd2565b90505b8115614c8e57614c8e84836130c9565b8015614c9e57614c9e88826130c9565b6040516311686e4b60e21b815233906345a1b92c90614cc3908b908b90600401615c58565b600060405180830381600087803b158015614cdd57600080fd5b505af1158015614cf1573d6000803e3d6000fd5b505050505050505050505050565b600080826001811115614d1457614d1461567d565b1415614d37576000858152601f60205260409020600a01805460ff191660011790555b6001826001811115614d4b57614d4b61567d565b1415614d6e576000858152601f60205260409020600a01805460ff191660021790555b6000806000856080015165ffffffffffff16421115614daa57614da7866080015165ffffffffffff1642614da29190615b9c565b6153be565b91505b6000856001811115614dbe57614dbe61567d565b1415614ddd57614dda8660a00151876000015189601c5461416e565b90505b614dea6298968082615bd2565b8660400151614df9919061561f565b93506064614e078386615bb3565b614e119190615bd2565b9250614e1d8385615b9c565b93506000856001811115614e3357614e3361567d565b1415614e4d57601860008154614e4890615c20565b909155505b6001856001811115614e6157614e6161567d565b1415614e7b57601960008154614e7690615c20565b909155505b8215614e995782601b6000828254614e93919061561f565b90915550505b6000856001811115614ead57614ead61567d565b1415614ecf576000888152601f60205260409020600681018590556008018390555b81888a6001600160a01b03167fbd866a3fbf35e201f790e87581b1afbb3165e879df5d35313a4875a70b9f3b368787604051614f15929190918252602082015260400190565b60405180910390a450505095945050505050565b6001600160a01b03841660009081526015602090815260408083208684529091529020548214614f7a576001600160a01b038416600090815260156020908152604080832086845290915290208290555b6001600160a01b0384166000908152601560209081526040808320868452909152902060010154811461218e576001600160a01b0384166000908152601560209081526040808320868452909152902060010181905550505050565b6000614fe28233610a8d565b90506000198114611f25578061500b57604051632048203360e01b815260040160405180910390fd5b6001600160a01b0382166000908152603c6020526040812090335b6001600160a01b03166001600160a01b031681526020019081526020016000206000815461505390615cb8565b909155505050565b600061506782336109c8565b90506000198114611f25578061509057604051632048203360e01b815260040160405180910390fd5b6001600160a01b0382166000908152603a602052604081209033615026565b60006040518061018001604052808b81526020018a61ffff1681526020018981526020014265ffffffffffff168152602001620151808b6150f09190615bb3565b6150fa904261561f565b65ffffffffffff1681526020018881526020018781526020016000815260200184815260200160008152602001838152602001600060028111156151405761514061567d565b90526001600160a01b038c166000908152601d6020526040812080549293509091829061516c90615c20565b91829055506001600160a01b038d166000908152601e6020908152604080832084845282528083208a815560019081018a90558a8452601f8352928190208651815591860151828401805461ffff90921661ffff199092169190911790558501516002808301919091556060860151600383018054608089015165ffffffffffff908116600160301b026001600160601b031990921693169290921791909117905560a0860151600483015560c0860151600583015560e08601516006830155610100860151600783015561012086015160088301556101408601516009830155610160860151600a83018054959650879593949193909260ff199091169190849081111561527d5761527d61567d565b021790555090505084868d6001600160a01b03167f2b2d6cbbd64131cfb538b30ae4a9dc516d6fe02b65e223719e216846f0118a95856040516152c09190615adb565b60405180910390a4505050505050505050505050565b60008383106153515760006152eb8585615b9c565b905060006152fd620151806007615bb3565b9050808211615311576000925050506118df565b6153486201518061532c6153258486615b9c565b60016154d4565b6153369190615bd2565b61534190600161561f565b60636154eb565b925050506118df565b60028260048111156153655761536561567d565b1415615373575060006118df565b600261537f8686615b9c565b6153899190615bd2565b615393908661561f565b4210156153b3576040516354d335b760e11b815260040160405180910390fd5b506032949350505050565b60006153ce620151806007615bb3565b82116153dc57506000919050565b620151806153ec6007600161561f565b6153f69190615bb3565b821161540457506001919050565b620151806154146007600261561f565b61541e9190615bb3565b821161542c57506003919050565b6201518061543c6007600361561f565b6154469190615bb3565b821161545457506008919050565b620151806154646007600461561f565b61546e9190615bb3565b821161547c57506011919050565b6201518061548c6007600561561f565b6154969190615bb3565b82116154a457506023919050565b620151806154b46007600661561f565b6154be9190615bb3565b82116154cc57506048919050565b506063919050565b6000818311156154e55750816112aa565b50919050565b6000818311156154fc5750806112aa565b5090919050565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a081018290529060c08201905b905290565b6040518060600160405280600081526020016000815260200161553b615503565b60405180610180016040528060008152602001600061ffff16815260200160008152602001600065ffffffffffff168152602001600065ffffffffffff1681526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000600281111561553b5761553b61567d565b604051806080016040528060008152602001600081526020016000815260200161553b615561565b634e487b7160e01b600052601160045260246000fd5b6000821982111561563257615632615609565b500190565b80356001600160a01b038116811461564e57600080fd5b919050565b6000806040838503121561566657600080fd5b61566f83615637565b946020939093013593505050565b634e487b7160e01b600052602160045260246000fd5b600381106113d6576113d661567d565b805182526020810151602083015261ffff6040820151166040830152606081015165ffffffffffff8082166060850152806080840151166080850152505060a081015160a083015260c08101516156f981615693565b8060c0840152505050565b60e081016112aa82846156a3565b60006020828403121561572457600080fd5b81356001600160e01b03198116811461177057600080fd5b600060208083528351808285015260005b818110156157695785810183015185820160400152820161574d565b8181111561577b576000604083870101525b50601f01601f1916929092016040019392505050565b6000602082840312156157a357600080fd5b5035919050565b6000806000606084860312156157bf57600080fd5b505081359360208301359350604090920135919050565b600080604083850312156157e957600080fd5b6157f283615637565b915061580060208401615637565b90509250929050565b60008060006060848603121561581e57600080fd5b61582784615637565b925061583560208501615637565b9150604084013590509250925092565b60006020828403121561585757600080fd5b61177082615637565b600080600080600060a0868803121561587857600080fd5b61588186615637565b94506020860135935060408601359250606086013591506158a460808701615637565b90509295509295909350565b600080604083850312156158c357600080fd5b50508035926020909101359150565b600080600080608085870312156158e857600080fd5b84359350602085013592506040850135915061590660608601615637565b905092959194509250565b6001600160a01b0391909116815260200190565b602080825282518282018190526000919060409081850190868401855b8281101561597c578151805185528681015187860152850151615967868601826156a3565b50610120939093019290850190600101615942565b5091979650505050505050565b602081016002831061599d5761599d61567d565b91905290565b600080600080608085870312156159b957600080fd5b6159c285615637565b966020860135965060408601359560600135945092505050565b80151581146113d657600080fd5b600080604083850312156159fd57600080fd5b615a0683615637565b91506020830135615a16816159dc565b809150509250929050565b615a2a81615693565b9052565b805182526020810151615a47602084018261ffff169052565b50604081015160408301526060810151615a6b606084018265ffffffffffff169052565b506080810151615a85608084018265ffffffffffff169052565b5060a081015160a083015260c081015160c083015260e081015160e08301526101008082015181840152506101208082015181840152506101408082015181840152506101608082015161218e82850182615a21565b61018081016112aa8284615a2e565b602080825282518282018190526000919060409081850190868401855b8281101561597c578151805185528681015187860152858101518686015260609081015190615b3881870183615a2e565b50506101e0939093019290850190600101615b07565b600181811c90821680615b6257607f821691505b602082108114156154e557634e487b7160e01b600052602260045260246000fd5b600060208284031215615b9557600080fd5b5051919050565b600082821015615bae57615bae615609565b500390565b6000816000190483118215151615615bcd57615bcd615609565b500290565b600082615bef57634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000600019821415615c3457615c34615609565b5060010190565b82815260408101615c4b83615693565b8260208301529392505050565b6001600160a01b03929092168252602082015260400190565b600060208284031215615c8357600080fd5b8151611770816159dc565b828152610100810161177060208301846156a3565b6001600160e01b031991909116815260200190565b600081615cc757615cc7615609565b50600019019056fec4f8f7f5ee45326dd80cc2262cf4948c0aa62c4ed9b775cbe9662ca3618994d3ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220e147f8db36d790d87b37772e1baf6c0932f5345d8bcbb31daca1afd7a334e2c864736f6c634300080a00330000000000000000000000006c3c3a22ab3725c017d41d31b23efaf51a8ece480000000000000000000000009bff9f810d19cdb4bf7701c9d5ad101e91cda08d000000000000000000000000f19308f923582a6f7c465e5ce7a9dc1bec6665b1000000000000000000000000a2d21205aa7273baddfc8e9551e05e23bb49ce4600000000000000000000000015e5b9b9adf208cc7ca3ae1e6a49506eb5f397dd

Deployed Bytecode

0x6080604052600436106104ae5760003560e01c8062281d14146104d8578062ae5faa146104fc57806301ffc9a71461052957806306fdde0314610559578063095ea7b31461057b5780630cbe28d61461059b57806310cc8ee7146105bb57806312065fe0146105db5780631371bb40146105ee57806313aad5101461063457806318160ddd146106495780631ae409c01461065e578063216630b4146106735780632277d1bd14610688578063236393851461069d57806323b5675f146106b257806323b872dd146106c757806324f33a5b146106e7578063276b5c1a146106fc57806329b70d7a146107115780632d02347a146107475780632f77195114610774578063300284f214610789578063313ce567146107a957806333f3fd78146107c557806337c4f8c4146107e5578063392e353d14610805578063395093511461083a578063398015271461085a5780633a9693e11461087a5780633c34267f1461089a5780633d05012d146108ba578063403b285b146108cf57806341d6831a146108f657806345a1b92c1461090b578063462a8c2f1461092b5780634cf2d48f146109585780634df833af14610978578063510f8b9c1461098d578063544a6c59146109ad57806354f5d028146109f3578063566d0be314610a085780635742744114610a285780635a3c8b7d14610a485780635c1c5cb314610a5d5780635c3ef13014610a725780636272683314610ab8578063682d2d1114610acd578063692b6fdc14610aed5780636c52876b14610b1a5780636f60963314610b2f5780636f9170f614610b4457806370a0823114610b7d57806370c9b00214610b9d578063715018a614610bbd5780637629052d14610bd25780637789281e14610bf257806377a5426914610c075780637b763a2c14610c275780637d6b325314610c475780637ec26dca14610c67578063800bb26914610c7c578063842e298114610c91578063856a73da14610cbe578063880a083614610cde57806389de416514610cf35780638b13cdae14610d1357806392c1df5414610d335780639332812414610d485780639474782014610d6857806395d89b4114610d7d57806396d9720814610d925780639a5a6cd914610db25780639ed9922014610dd2578063a2573e5b14610df2578063a457c2d714610e10578063a76222af14610e30578063a9059cbb14610e45578063af4fb76314610e65578063af835b8a14610e7a578063b0977f6914610ea7578063b0aebfbc14610ebc578063b8fac78914610edc578063b984c94614610ef1578063baf20eef14610f06578063bb88603c14610f26578063c081f4c014610f3b578063c50312ad14610f50578063d16baeb914610f86578063d6f8688f14610fa6578063d819e19814610fe6578063d9af94af14611013578063dd62ed3e14611026578063dff96e9a14611046578063e33a3c941461105b578063e3af6d0a1461107b578063e3d3227d146110ae578063e8052174146110c3578063efe17023146110e3578063f2fde38b14611103578063f63ec50e14611123578063f80b0cfb14611138578063faa94d3b1461114d578063fac940f41461116d578063fbf9529d14611180578063fe9497a614611195578063ffb75cab146111b557600080fd5b366104d35734156104d15734603760008282546104cb919061561f565b90915550505b005b600080fd5b3480156104e457600080fd5b50600b545b6040519081526020015b60405180910390f35b34801561050857600080fd5b5061051c610517366004615653565b6111e2565b6040516104f39190615704565b34801561053557600080fd5b50610549610544366004615712565b6112b0565b60405190151581526020016104f3565b34801561056557600080fd5b5061056e6112e6565b6040516104f3919061573c565b34801561058757600080fd5b50610549610596366004615653565b611378565b3480156105a757600080fd5b506104d16105b6366004615791565b611390565b3480156105c757600080fd5b506104d16105d63660046157aa565b6113d9565b3480156105e757600080fd5b50476104e9565b3480156105fa57600080fd5b506104e96106093660046157d6565b6001600160a01b039182166000908152602f6020908152604080832093909416825291909152205490565b34801561064057600080fd5b506104d161154e565b34801561065557600080fd5b506002546104e9565b34801561066a57600080fd5b506006546104e9565b34801561067f57600080fd5b50601a546104e9565b34801561069457600080fd5b506104d1611558565b3480156106a957600080fd5b506104e961172d565b3480156106be57600080fd5b50600d546104e9565b3480156106d357600080fd5b506105496106e2366004615809565b611751565b3480156106f357600080fd5b50600e546104e9565b34801561070857600080fd5b506026546104e9565b34801561071d57600080fd5b506104e961072c366004615845565b6001600160a01b03166000908152602e602052604090205490565b34801561075357600080fd5b506104e9610762366004615791565b60009081526016602052604090205490565b34801561078057600080fd5b506019546104e9565b34801561079557600080fd5b506105496107a4366004615653565b611777565b3480156107b557600080fd5b50604051601281526020016104f3565b3480156107d157600080fd5b506104d16107e0366004615860565b6117f8565b3480156107f157600080fd5b506104d1610800366004615791565b611826565b34801561081157600080fd5b506108256108203660046158b0565b61184f565b604080519283526020830191909152016104f3565b34801561084657600080fd5b50610549610855366004615653565b611873565b34801561086657600080fd5b506104e96108753660046158d2565b611895565b34801561088657600080fd5b506104d1610895366004615845565b6118e7565b3480156108a657600080fd5b506104d16108b5366004615791565b611964565b3480156108c657600080fd5b506037546104e9565b3480156108db57600080fd5b506032546001600160a01b03165b6040516104f39190615911565b34801561090257600080fd5b506036546104e9565b34801561091757600080fd5b506104d1610926366004615653565b6119d3565b34801561093757600080fd5b506104e9610946366004615791565b60009081526010602052604090205490565b34801561096457600080fd5b506104d1610973366004615845565b611a36565b34801561098457600080fd5b506038546104e9565b34801561099957600080fd5b506104e96109a8366004615845565b611a87565b3480156109b957600080fd5b506104e96109c83660046157d6565b6001600160a01b039182166000908152603a6020908152604080832093909416825291909152205490565b3480156109ff57600080fd5b50602c546104e9565b348015610a1457600080fd5b506104d1610a23366004615791565b611aef565b348015610a3457600080fd5b50610825610a433660046158b0565b611b2a565b348015610a5457600080fd5b506009546104e9565b348015610a6957600080fd5b506104d1611b6d565b348015610a7e57600080fd5b506104e9610a8d3660046157d6565b6001600160a01b039182166000908152603c6020908152604080832093909416825291909152205490565b348015610ac457600080fd5b506104d1611bad565b348015610ad957600080fd5b506104e9610ae8366004615653565b611cc1565b348015610af957600080fd5b506104e9610b08366004615791565b60009081526011602052604090205490565b348015610b2657600080fd5b506104e9611e2a565b348015610b3b57600080fd5b506008546104e9565b348015610b5057600080fd5b50610549610b5f366004615845565b6001600160a01b03166000908152603b602052604090205460ff1690565b348015610b8957600080fd5b506104e9610b98366004615845565b611e42565b348015610ba957600080fd5b50610825610bb83660046158b0565b611e5d565b348015610bc957600080fd5b506104d1611e81565b348015610bde57600080fd5b506104d1610bed366004615845565b611e93565b348015610bfe57600080fd5b506022546104e9565b348015610c1357600080fd5b506104d1610c22366004615860565b611ee4565b348015610c3357600080fd5b506104d1610c42366004615653565b611f01565b348015610c5357600080fd5b506104d1610c62366004615845565b611f29565b348015610c7357600080fd5b506017546104e9565b348015610c8857600080fd5b506104e9611f7a565b348015610c9d57600080fd5b50610cb1610cac366004615845565b611f92565b6040516104f39190615925565b348015610cca57600080fd5b50610549610cd9366004615653565b612091565b348015610cea57600080fd5b506021546104e9565b348015610cff57600080fd5b50610825610d0e366004615653565b612112565b348015610d1f57600080fd5b506104e9610d2e366004615791565b612142565b348015610d3f57600080fd5b50601c546104e9565b348015610d5457600080fd5b50600f5460ff166040516104f39190615989565b348015610d7457600080fd5b506104e961214d565b348015610d8957600080fd5b5061056e612158565b348015610d9e57600080fd5b506104d1610dad3660046159a3565b612167565b348015610dbe57600080fd5b506104e9610dcd366004615845565b612194565b348015610dde57600080fd5b506104d1610ded3660046159a3565b6121cf565b348015610dfe57600080fd5b506031546001600160a01b03166108e9565b348015610e1c57600080fd5b50610549610e2b366004615653565b6121ec565b348015610e3c57600080fd5b506104e9612272565b348015610e5157600080fd5b50610549610e60366004615653565b6122e4565b348015610e7157600080fd5b506104e96122f2565b348015610e8657600080fd5b506104e9610e95366004615791565b60009081526012602052604090205490565b348015610eb357600080fd5b506023546104e9565b348015610ec857600080fd5b506104d1610ed7366004615845565b612304565b348015610ee857600080fd5b506025546104e9565b348015610efd57600080fd5b506020546104e9565b348015610f1257600080fd5b506104d1610f21366004615791565b612381565b348015610f3257600080fd5b506104d16123a5565b348015610f4757600080fd5b506024546104e9565b348015610f5c57600080fd5b506104e9610f6b366004615845565b6001600160a01b03166000908152601d602052604090205490565b348015610f9257600080fd5b506104d1610fa13660046159ea565b6123ff565b348015610fb257600080fd5b50610fc6610fc1366004615653565b612459565b6040805194855260208501939093529183015260608201526080016104f3565b348015610ff257600080fd5b50611006611001366004615653565b61257a565b6040516104f39190615adb565b34801561101f57600080fd5b50426104e9565b34801561103257600080fd5b506104e96110413660046157d6565b61265f565b34801561105257600080fd5b50601b546104e9565b34801561106757600080fd5b506104e9611076366004615845565b61268a565b34801561108757600080fd5b507f0000000000000000000000000000000000000000000000000000000065df437f6104e9565b3480156110ba57600080fd5b506104d16126e9565b3480156110cf57600080fd5b506104e96110de366004615845565b612783565b3480156110ef57600080fd5b506104d16110fe366004615653565b61279e565b34801561110f57600080fd5b506104d161111e366004615845565b6127d0565b34801561112f57600080fd5b50600a546104e9565b34801561114457600080fd5b506018546104e9565b34801561115957600080fd5b506104e9611168366004615845565b6127e1565b6104d161117b3660046157aa565b6127fc565b34801561118c57600080fd5b506007546104e9565b3480156111a157600080fd5b506104e96111b0366004615845565b61292b565b3480156111c157600080fd5b506111d56111d0366004615845565b61298a565b6040516104f39190615aea565b6111ea615503565b6001600160a01b038316600090815260286020908152604080832085845282528083205483526029825291829020825160e0810184528154815260018201549281019290925260028082015461ffff81169484019490945265ffffffffffff62010000850481166060850152600160401b9094049093166080830152600381015460a083015260048101549192909160c084019160ff909116908111156112935761129361567d565b60028111156112a4576112a461567d565b90525090505b92915050565b60006001600160e01b031982166301ffc9a760e01b14806112aa57506001600160e01b031982166311686e4b60e21b1492915050565b6060600380546112f590615b4e565b80601f016020809104026020016040519081016040528092919081815260200182805461132190615b4e565b801561136e5780601f106113435761010080835404028352916020019161136e565b820191906000526020600020905b81548152906001019060200180831161135157829003601f168201915b5050505050905090565b600033611386818585612a9b565b5060019392505050565b611398612bbf565b6113a0612e0d565b6113cc336113c733846113b260065490565b600160036113c2600f5460ff1690565b612e67565b6130c9565b6113d66001600555565b50565b6113e1612bbf565b6113e9612e0d565b826113f333611e42565b101561141257604051631d78718160e01b815260040160405180910390fd5b33321461147957336000908152603b602052604090205460ff166114795760405162461bcd60e51b815260206004820152601960248201527821b7b73a3930b1ba103737ba103bb434ba32b634b9ba32b21760391b60448201526064015b60405180910390fd5b80156114885761148881613176565b6114923384613207565b61153f3361153a3386866114a560075490565b600654600f5460ff168960008b116114be576000613327565b603360009054906101000a90046001600160a01b03166001600160a01b031663665f8efb6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611511573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115359190615b83565b613327565b61345e565b6115496001600555565b505050565b611556612bbf565b565b611560612bbf565b611568612e0d565b600061157360225490565b6021546115809190615b9c565b905060018110156115a45760405163df8afe1960e01b815260040160405180910390fd5b6000806000806036546000146115c8576115bc61359b565b92965090945090925090505b60006115d360065490565b9050600060016115e5601689856137a9565b60018111156115f6576115f661567d565b148015611614575060008160018111156116125761161261567d565b145b61161e5780611623565b506001805b506001611632604589856137a9565b60018111156116435761164361567d565b1480156116615750600081600181111561165f5761165f61567d565b145b61166b5780611670565b506001805b5060016116806101a489856137a9565b60018111156116915761169161567d565b1480156116af575060008160018111156116ad576116ad61567d565b145b6116b957806116be565b506001805b5060018160018111156116d3576116d361567d565b141561170a576000600f5460ff1660018111156116f2576116f261567d565b141561170a5761170a600f805460ff19166001179055565b851561171c5761171c8686858761384f565b505050505050506115566001600555565b60006019546018546017546117429190615b9c565b61174c9190615b9c565b905090565b60003361175f85828561389a565b61176a85858561390e565b60019150505b9392505050565b60006001600160a01b0383166117a0576040516329d6af1160e01b815260040160405180910390fd5b336000818152603a602090815260408083206001600160a01b038816808552925280832086905551859391927ff8e109bcddf5e12132b7cd8a8517d97498f50c7ac595874d6f513243098b079891a450600192915050565b611800612bbf565b611808612e0d565b6118158585858585613aa0565b61181f6001600555565b5050505050565b61182e612bbf565b611836612e0d565b6113cc33600061184833856001613b15565b6001613d13565b60009182526014602090815260408084209284529190529020600181015490549091565b600033611386818585611886838361265f565b611890919061561f565b612a9b565b6000806118aa866118a560085490565b613e25565b905060006118b88583613e35565b90506118da87876118c860095490565b600b546118d489611a87565b86613e4f565b925050505b949350505050565b6031546001600160a01b0316336001600160a01b03161461191b576040516339e2f41f60e21b815260040160405180910390fd5b6001600160a01b038116611942576040516329d6af1160e01b815260040160405180910390fd5b603180546001600160a01b0319166001600160a01b0392909216919091179055565b61196c612bbf565b611974612e0d565b8061199257604051630aa71b7760e41b815260040160405180910390fd5b8061199c33611e42565b10156119bb57604051631d78718160e01b815260040160405180910390fd5b6119c53382613207565b6113cc336000836000613d13565b6035546001600160a01b03163314611a1b5760405162461bcd60e51b815260206004820152600b60248201526a09edcd8f240a8d2e8c2dcb60ab1b6044820152606401611470565b8060386000828254611a2d919061561f565b90915550505050565b611a3e613f7e565b6001600160a01b038116611a65576040516329d6af1160e01b815260040160405180910390fd5b603480546001600160a01b0319166001600160a01b0392909216919091179055565b600080611a9383612783565b905080611aa35750600092915050565b680204fce5e3e2502611601f1b8110611ac65750676f05b59d3b20000092915050565b680204fce5e3e2502611601f1b611ae582676f05b59d3b200000615bb3565b6117709190615bd2565b611af7612bbf565b611aff612e0d565b6113cc336000611b233385611b1360065490565b600260036113c2600f5460ff1690565b6002613d13565b6000806000611b3860075490565b9050611b45858583613fa9565b9250611b59670de0b6b3a764000082615bd2565b611b639086615bd2565b9150509250929050565b611b75612bbf565b611b7d612e0d565b600080600080611b8b61359b565b9350935093509350611b9f8484848461384f565b505050506115566001600555565b611bb5612bbf565b611bbd612e0d565b600080600080611bcd6016614000565b9092509050611bdc828561561f565b9350611be8818461561f565b9250611bf46045614000565b9092509050611c03828561561f565b9350611c0f818461561f565b9250611c1c6101a4614000565b9092509050611c2b828561561f565b9350611c37818461561f565b925083158015611c45575082155b15611c63576040516354d2b34960e01b815260040160405180910390fd5b8315611c7457611c74335b85614037565b8215611c8457611c8433846140d3565b6040518390859033907ff01da32686223933d8a18a391060918c7f11a3648639edd87ae013e2e273174390600090a4505050506115566001600555565b6001600160a01b0382166000908152601e602090815260408083208484529091528120805460019091015481611d0a57604051637a2e11eb60e01b815260040160405180910390fd5b6000828152601f6020908152604080832081516101808101835281548152600182015461ffff169381019390935260028082015492840192909252600381015465ffffffffffff8082166060860152600160301b909104166080840152600481015460a0840152600581015460c0840152600681015460e0840152600781015461010084015260088101546101208401526009810154610140840152600a810154909161016084019160ff1690811115611dc657611dc661567d565b6002811115611dd757611dd761567d565b815250509050806040015193506000611dfc8260a00151836000015185601c5461416e565b9050611e0b6298968082615bd2565b611e15908661561f565b611e1f908661561f565b979650505050505050565b6000611e3560245490565b601b5461174c919061561f565b6001600160a01b031660009081526020819052604090205490565b60009182526013602090815260408084209284529190529020600181015490549091565b611e89613f7e565b61155660006141be565b611e9b613f7e565b6001600160a01b038116611ec2576040516329d6af1160e01b815260040160405180910390fd5b603580546001600160a01b0319166001600160a01b0392909216919091179055565b611eec612bbf565b611ef4612e0d565b61181585858585856141e0565b611f09612bbf565b611f11612e0d565b611f1b828261420f565b611f256001600555565b5050565b611f31613f7e565b6001600160a01b038116611f58576040516329d6af1160e01b815260040160405180910390fd5b603380546001600160a01b0319166001600160a01b0392909216919091179055565b6000611f8560255490565b60205461174c9190615b9c565b6001600160a01b038116600090815260276020526040812054606091816001600160401b03811115611fc657611fc6615bf4565b604051908082528060200260200182016040528015611fff57816020015b611fec615540565b815260200190600190039081611fe45790505b50905060015b82811161208957604080516060810182528281526001600160a01b038716600090815260286020908152838220858352815290839020549082015290810161204d87846111e2565b90528261205b600184615b9c565b8151811061206b5761206b615c0a565b6020026020010181905250808061208190615c20565b915050612005565b509392505050565b60006001600160a01b0383166120ba576040516329d6af1160e01b815260040160405180910390fd5b336000818152603c602090815260408083206001600160a01b038816808552925280832086905551859391927fd508e6bf29a4128e58df993e4fe1db1d926db54e85247bc919df2c52eb78212591a450600192915050565b6001600160a01b039190911660009081526015602090815260408083209383529290522080546001909101549091565b60006112aa8261426d565b600061174c30611e42565b6060600480546112f590615b4e565b61216f612bbf565b612177612e0d565b61218484848484336141e0565b61218e6001600555565b50505050565b6001600160a01b0381166000908152602b60205260408120816121b6846127e1565b8152602001908152602001600020600101549050919050565b6121d7612bbf565b6121df612e0d565b6121848484848433613aa0565b600033816121fa828661265f565b90508381101561225a5760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401611470565b6122678286868403612a9b565b506001949350505050565b6035546040516370a0823160e01b81526000916001600160a01b0316906370a08231906122a3903090600401615911565b602060405180830381865afa1580156122c0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061174c9190615b83565b60003361138681858561390e565b600060225460215461174c9190615b9c565b6032546001600160a01b0316336001600160a01b031614612338576040516339e2f41f60e21b815260040160405180910390fd5b6001600160a01b03811661235f576040516329d6af1160e01b815260040160405180910390fd5b603280546001600160a01b0319166001600160a01b0392909216919091179055565b612389612bbf565b612391612e0d565b6113cc6123a033836000613b15565b6142ce565b6123ad612bbf565b6033546001600160a01b0316336001600160a01b0316146123e157604051630eb3f67960e21b815260040160405180910390fd5b603354611556906001600160a01b03166123fa81611e42565b613207565b612407613f7e565b6001600160a01b03821661242e576040516329d6af1160e01b815260040160405180910390fd5b6001600160a01b03919091166000908152603b60205260409020805460ff1916911515919091179055565b60008060008060006124778660009081526012602052604090205490565b90506124838787612112565b90935091506000612493886127e1565b9050835b82811161256e576000806124ab8a84611e5d565b9150915060006124bb8b8561184f565b50905060006124cc8d8988866142f6565b9850905083158015906124de57508015155b1561250d57670de0b6b3a76400006124f68583615bb3565b6125009190615bd2565b61250a908c61561f565b9a505b811580159061251b57508015155b1561254a57670de0b6b3a76400006125338383615bb3565b61253d9190615bd2565b612547908b61561f565b99505b61255585600161561f565b985050505050808061256690615c20565b915050612497565b50505092959194509250565b612582615561565b6001600160a01b0383166000908152601e602090815260408083208584528252808320548352601f82529182902082516101808101845281548152600182015461ffff169281019290925260028082015493830193909352600381015465ffffffffffff8082166060850152600160301b909104166080830152600481015460a0830152600581015460c0830152600681015460e0830152600781015461010083015260088101546101208301526009810154610140830152600a8101549192909161016084019160ff909116908111156112935761129361567d565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b600080612698836016612459565b509092506126aa91508290508361561f565b91506126b7836045612459565b509092506126c991508290508361561f565b91506126d7836101a4612459565b5090925061177091508290508361561f565b6033546001600160a01b0316336001600160a01b03161461271d576040516339e2f41f60e21b815260040160405180910390fd5b600160395460ff1660018111156127365761273661567d565b141561275557604051633ee45dcb60e21b815260040160405180910390fd5b6039805460ff19166001179055603354611556906001600160a01b03166714adf4b7320334b9601e1b6130c9565b6001600160a01b03166000908152602d602052604090205490565b6127a6612bbf565b6127ae612e0d565b611f1b826113c784846127c060065490565b600160046113c2600f5460ff1690565b6127d8613f7e565b6113d6816141be565b6001600160a01b03166000908152602a602052604090205490565b612804612e0d565b61280c612bbf565b6103e861281833610f6b565b61282390600161561f565b1115612842576040516334da899760e11b815260040160405180910390fd5b80156128515761285181613176565b60006040518060c001604052808581526020018481526020018381526020018561287a601c5490565b612884919061561f565b815260200161289260175490565b61289d90600161561f565b81526020016128af866118a560085490565b9052905060006128eb3383516020850151600954600a54600b546128d233611a87565b89606001518a608001518b60a001518c6040015161437f565b601a546128f8919061561f565b90506129168260800151836060015183601792909255601c55601a55565b61291f85614420565b50506115496001600555565b600080612939836016612459565b5091925061294b91508290508361561f565b9150612958836045612459565b5091925061296a91508290508361561f565b9150612978836101a4612459565b5091925061177091508290508361561f565b6001600160a01b0381166000908152601d6020526040902054606090806001600160401b038111156129be576129be615bf4565b6040519080825280602002602001820160405280156129f757816020015b6129e46155e1565b8152602001906001900390816129dc5790505b50915060015b818111612a9457604080516080810182528281526001600160a01b0386166000908152601e6020908152838220858352808252848320805483860152928690529052600101549181019190915260608101612a58868461257a565b905283612a66600184615b9c565b81518110612a7657612a76615c0a565b60200260200101819052508080612a8c90615c20565b9150506129fd565b5050919050565b6001600160a01b038316612afd5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401611470565b6001600160a01b038216612b5e5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401611470565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b600654600062015180612bf27f0000000000000000000000000000000000000000000000000000000065df437f42615b9c565b612bfc9190615bd2565b612c0790600161561f565b905081811115611f2557600854600954600a54600b546000612c298787615b9c565b905060005b81811015612de957612710612c4561271a88615bb3565b612c4f9190615bd2565b9550612710612c606126ed87615bb3565b612c6a9190615bd2565b9450612710612c7b6126ed86615bb3565b612c859190615bd2565b935068327cb2734119d3b7a9601f1b861115612cab5768327cb2734119d3b7a9601f1b95505b600c548810158015612cbe5750600d5415155b8015612cce5750611b58600e5414155b15612d20576032600d6000828254612ce69190615b9c565b925050819055506032600e6000828254612d00919061561f565b925050819055506007600c6000828254612d1a919061561f565b90915550505b6958f03ee118a13e800000851015612d40576958f03ee118a13e80000094505b612d4e6103e8612710615bb3565b841015612d6657612d636103e8612710615bb3565b93505b60458711612d8257612d7b6202361f84615b9c565b9250612d87565b600092505b85612d9189615c20565b604080518881526020810188905290810186905290995089907f1b4cf4ae62850d5107d63ca4895b7d62305df42148813646f7f4a76638a9618a9060600160405180910390a380612de181615c20565b915050612c2e565b5050600893909355600991909155600a55600b5560065550600f805460ff19169055565b60026005541415612e605760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401611470565b6002600555565b6001600160a01b038616600090815260286020908152604080832088845290915281205480612ea95760405163de9d71e560e01b815260040160405180910390fd5b6000818152602960209081526040808320815160e0810183528154815260018201549381019390935260028082015461ffff81169385019390935265ffffffffffff62010000840481166060860152600160401b9093049092166080840152600381015460a08401526004810154909160c084019160ff1690811115612f3157612f3161567d565b6002811115612f4257612f4261567d565b905250905060018160c001516002811115612f5f57612f5f61567d565b1415612f7e5760405163081b599f60e41b815260040160405180910390fd5b60028160c001516002811115612f9657612f9661567d565b1415612fb55760405163c3fb2f4760e01b815260040160405180910390fd5b6004856004811115612fc957612fc961567d565b148015612fe15750806080015165ffffffffffff1642105b15612fff57604051639d2f92f560e01b815260040160405180910390fd5b60208101518151613016908b9083908b898c614508565b50600187600481111561302b5761302b61567d565b14156130655760256000815461304090615c20565b909155506000838152602960205260409020600401805460ff191660011790556130af565b60028760048111156130795761307961567d565b14156130af5760266000815461308e90615c20565b909155506000838152602960205260409020600401805460ff191660021790555b6130bb8a84848a6146b3565b9a9950505050505050505050565b6001600160a01b03821661311f5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401611470565b8060026000828254613131919061561f565b90915550506001600160a01b03821660008181526020818152604080832080548601905551848152600080516020615cf0833981519152910160405180910390a35050565b73f19308f923582a6f7c465e5ce7a9dc1bec6665b16333f3fd783360315460405160e084901b6001600160e01b03191681526001600160a01b03928316600480830191909152602482018790526044820181905260648201529116608482015260a401600060405180830381600087803b1580156131f357600080fd5b505af115801561181f573d6000803e3d6000fd5b6001600160a01b0382166132675760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401611470565b6001600160a01b038216600090815260208190526040902054818110156132db5760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401611470565b6001600160a01b038316600081815260208181526040808320868603905560028054879003905551858152919291600080516020615cf0833981519152910160405180910390a3505050565b6001600160a01b03881660009081526027602052604081208054829190829061334f90615c20565b918290555090506103e88111156133795760405163174a8f9960e21b815260040160405180910390fd5b601e881080613389575061033e88115b156133a75760405163d1b1ca9d60e01b815260040160405180910390fd5b60006133b48a8a8a613fa9565b905060016133ca670de0b6b3a764000083615bd2565b10156133e95760405163082aaa8760e41b815260040160405180910390fd5b84156134325760006133fc86868d614775565b90506107d081111561340d57506107d05b61271061341a8284615bb3565b6134249190615bd2565b61342e908361561f565b9150505b613440828c8c8c858a6147ba565b61344f8b828c8a8a6000614508565b9b9a5050505050505050505050565b8060011415611f255760166000526012602052600080516020615cd08339815191525415611f255760166000526012602052600080516020615cd0833981519152546134ab90600161561f565b6001600160a01b038316600090815260156020908152604080832060168452825282209290925560459052601290527f2438ba9a9fcd62a1364c4e295291b48be211763142dce8144a28ac91969f666f5461350790600161561f565b6001600160a01b03831660009081526015602090815260408083206045845282528220929092556101a49052601290527fb7ff304c5ab3d0f93c4868e4aee66ebe077d23da2da94c4b2280ba2f567bd0225461356490600161561f565b6001600160a01b03831660009081526015602090815260408083206101a4845290915290206001600160601b039190911690555050565b6000806000806000603654905080600014156135ca57604051630b0daf9d60e21b815260040160405180910390fd5b60006036819055604051829133917f97934fea6c25f21bceb486fba06c291987ec0bc4e293ea7b328285bdb07a180f9190a3620f424061360c610bb883615bb3565b6136169190615bd2565b94506136228582615b9c565b9050612710613630600d5490565b61363a9083615bb3565b6136449190615bd2565b9350612710613652600e5490565b61365c9083615bb3565b6136669190615bd2565b925061271061367660c883615bb3565b6136809190615bd2565b9150600082846136908785615b9c565b61369a9190615b9c565b6136a49190615b9c565b9050801561371f5760006127106136bd610dac84615bb3565b6136c79190615bd2565b905060006127106136da610bb885615bb3565b6136e49190615bd2565b90506136f1601683614971565b6136fc604582614971565b61371c6101a48261370d8587615b9c565b6137179190615b9c565b614971565b50505b60375480156137a0576000603781905561271061373e610dac84615bb3565b6137489190615bd2565b9050600061271061375b610bb885615bb3565b6137659190615bd2565b905061377260168361498f565b61377d60458261498f565b61379d6101a48261378e8587615b9c565b6137989190615b9c565b61498f565b50505b50505090919293565b6000838152601660205260408120548210156137c757506000611770565b6137d0846149ad565b600084815260106020908152604080832054601190925290912054811580156137f7575080155b1561380757600092505050611770565b61381386838388614a18565b506040518290879033907f973833b060ae272a085ff5b17b764e39ddac6a5612bdc9337f586dc7efee604990600090a450600195945050505050565b61385833611c6e565b60315461386e906001600160a01b031682614037565b603354613884906001600160a01b031684614037565b60345461218e906001600160a01b031683614037565b60006138a6848461265f565b9050600019811461218e57818110156139015760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401611470565b61218e8484848403612a9b565b6001600160a01b0383166139725760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401611470565b6001600160a01b0382166139d45760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401611470565b6001600160a01b03831660009081526020819052604090205481811015613a4c5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401611470565b6001600160a01b0384811660008181526020818152604080832087870390559387168083529184902080548701905592518581529092600080516020615cf0833981519152910160405180910390a361218e565b83613abe57604051630aa71b7760e41b815260040160405180910390fd5b83613ac886611e42565b1015613ae757604051631d78718160e01b815260040160405180910390fd5b613af285338661389a565b613afc8383614ae1565b613b068585613207565b61181f85858585856000614c01565b6001600160a01b0383166000908152601e602090815260408083208584529091528120805460019091015481613b5e57604051637a2e11eb60e01b815260040160405180910390fd5b6000828152601f6020908152604080832081516101808101835281548152600182015461ffff169381019390935260028082015492840192909252600381015465ffffffffffff8082166060860152600160301b909104166080840152600481015460a0840152600581015460c0840152600681015460e0840152600781015461010084015260088101546101208401526009810154610140840152600a810154909161016084019160ff1690811115613c1a57613c1a61567d565b6002811115613c2b57613c2b61567d565b905250905060018161016001516002811115613c4957613c4961567d565b1415613c68576040516339f4887960e21b815260040160405180910390fd5b60028161016001516002811115613c8157613c8161567d565b1415613ca0576040516323acf12560e01b815260040160405180910390fd5b42816080015165ffffffffffff16118015613ccc57506000856001811115613cca57613cca61567d565b145b15613cea57604051636971be5760e11b815260040160405180910390fd5b8060400151601a6000828254613d009190615b9c565b90915550611e1f90508784848489614cff565b6001600160a01b0384166000908152602d602052604081208054849290613d3b90849061561f565b9250508190555081602c6000828254613d54919061561f565b90915550506001600160a01b03831615613dd2576001600160a01b0383166000908152602e602052604081208054849290613d9090849061561f565b90915550506001600160a01b038084166000908152602f6020908152604080832093881683529290529081208054849290613dcc90849061561f565b90915550505b826001600160a01b0316846001600160a01b03167fdf82885dedaf240ee119dba88f242ab92b4bff124f1c029d077cad9a57fb97c68484604051613e17929190615c3b565b60405180910390a350505050565b6000620186a0611ae58484615bb3565b60008082613e4585612710615bb3565b6118df9190615bd2565b60008086613e5d8988615bb3565b613e679190615bb3565b905086600114613eab57612710613e7f600189615b9c565b613e8a600b84615bb3565b613e949190615bb3565b613e9e9190615bd2565b613ea89082615b9c565b90505b9050808415613ee557620f42406064613ec48784615bb3565b613ece9190615bd2565b613ed89190615bd2565b613ee2908361561f565b91505b8315613f2157670de0b6b3a76400006064613f008684615bb3565b613f0a9190615bd2565b613f149190615bd2565b613f1e908361561f565b91505b8215613f65576103e88311613f365782613f3a565b6103e85b92506000612710613f4b8585615bb3565b613f559190615bd2565b9050613f61818461561f565b9250505b613f72620186a083615bd2565b98975050505050505050565b6030546001600160a01b0316331461155657604051635e8a044f60e01b815260040160405180910390fd5b60008364174876e800613fbb8561426d565b613fc59083615bb3565b613fcf9190615bd2565b613fd9908261561f565b9050613fed670de0b6b3a764000084615bd2565b613ff79082615bd2565b95945050505050565b600080808080806140113388612459565b935093509350935061402b6140233390565b888484614f29565b50919590945092505050565b6001600160a01b03821661405e576040516329d6af1160e01b815260040160405180910390fd5b60355460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb906140909085908590600401615c58565b6020604051808303816000875af11580156140af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115499190615c71565b6001600160a01b0382166140fa576040516329d6af1160e01b815260040160405180910390fd5b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114614147576040519150601f19603f3d011682016040523d82523d6000602084013e61414c565b606091505b505090508061154957604051633ad7296360e11b815260040160405180910390fd5b600082821161417f575060006118df565b620186a0670de0b6b3a76400006141968585615b9c565b6141a08789615bb3565b6141aa9190615bb3565b6141b49190615bb3565b613ff79190615bd2565b603080546001600160a01b0319166001600160a01b0392909216919091179055565b6141e985614fd6565b6141f38383614ae1565b61181f856142058787611b1360065490565b8585856002614c01565b6142188261505b565b614223600080614ae1565b600061423183836001613b15565b60315490915061425d906001600160a01b031661271061425384610320615bb3565b6113c79190615bd2565b6115498382600080336001614c01565b6000601e82116142815750620f4240919050565b600061428e601e84615b9c565b9050600061429f601e61033e615b9c565b8211156142b8576142b3601e61033e615b9c565b6142ba565b815b9050610190613e4564174876e80083615bb3565b6142d833826130c9565b6032546113d6906001600160a01b0316612710614253846064615bb3565b600080845b848111614374576001600160a01b0387166000908152602b60209081526040808320848452909152902054841061435a576001600160a01b0387166000908152602b60209081526040808320848452909152902060010154925061435f565b614374565b9450848061436c81615c20565b9150506142fb565b509095939450505050565b600089158061438e575060fa8a115b156143ac576040516332c13ef760e01b815260040160405180910390fd5b8a15806143bb5750620186a08b115b156143d9576040516304c7176560e31b815260040160405180910390fd5b600082156143ee576143eb8385613e35565b90505b6143fc8c8c8c8b8b86613e4f565b91506144108d8d8d858d8d8b8d8c8c6150af565b509b9a5050505050505050505050565b600061442f826118a560085490565b6035549091506001600160a01b03166323b872dd336040516001600160e01b031960e084901b1681526001600160a01b039091166004820152306024820152604481018490526064016020604051808303816000875af1158015614497573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144bb9190615c71565b5080603660008282546144ce919061561f565b909155505060065460405182919033907fd833e83f161e4ddfb1306cdf11a374a0a23393f008f9394b85999b988c232e3690600090a45050565b6001600160a01b0386166000908152602a6020908152604080832054602b83528184208185529092528220600101548284600481111561454a5761454a61567d565b14156145d4578161455a57600192505b614564888261561f565b6001600160a01b038a166000908152602b602052604081209061458685615c20565b94508481526020019081526020016000206001018190555087602160008282546145b0919061561f565b9250508190555086602360008282546145c9919061561f565b909155506146499050565b6145de8882615b9c565b6001600160a01b038a166000908152602b602052604081209061460085615c20565b945084815260200190815260200160002060010181905550876022600082825461462a919061561f565b9250508190555086602360008282546146439190615b9c565b90915550505b600085600181111561465d5761465d61567d565b146146725761466d86600161561f565b614674565b855b6001600160a01b039099166000818152602b6020908152604080832086845282528083209c909c55918152602a90915298909820559695505050505050565b6000808360000151905060006146e3856060015165ffffffffffff16866080015165ffffffffffff1642876152d6565b9050600060646146f38385615bb3565b6146fd9190615bd2565b90506147098184615b9c565b9350806024600082825461471d919061561f565b90915550506040805185815260208101839052839189916001600160a01b038c16917f971d9ff3287b3ba75194105e7281e55c93b0a89cad9915664bb3fd9211f8d5f1910160405180910390a4505050949350505050565b600080670de0b6b3a764000061478b8585615bb3565b6147959190615bd2565b90506000816147a661271088615bb3565b6147b09190615bd2565b9695505050505050565b60006020600081546147cb90615c20565b9182905550905060006147e16201518086615bb3565b6147eb904261561f565b905060006040518060e001604052808881526020018681526020018761ffff1681526020014265ffffffffffff1681526020018365ffffffffffff168152602001858152602001600060028111156148455761484561567d565b90526001600160a01b03891660009081526028602090815260408083208d845282528083208790558683526029825291829020835181559083015160018083019190915591830151600280830180546060870151608088015161ffff9095166001600160401b0319909216919091176201000065ffffffffffff928316021765ffffffffffff60401b1916600160401b91909416029290921790915560a0840151600383015560c0840151600483018054959650869593949193909260ff199091169190849081111561491a5761491a61567d565b021790555090505082886001600160a01b03167f04e1761ddc1451be2b9abb20d50c7e317778879612685874154566bbb4acafc7888460405161495e929190615c8e565b60405180910390a3505050505050505050565b60008281526010602052604081208054839290611a2d90849061561f565b60008281526011602052604081208054839290611a2d90849061561f565b60008181526016602052604090205460065481811061154957826149d18383615b9c565b6149db9190615bd2565b6149e690600161561f565b6149f09084615bb3565b60008481526016602052604081208054909190614a0e90849061561f565b9091555050505050565b6000848152601060209081526040808320839055601182528083208390556012909152812080548290614a4a90615c20565b9182905550905081614a64670de0b6b3a764000086615bb3565b614a6e9190615bd2565b600086815260136020908152604080832085845290915290206001810191909155600654905581614aa7670de0b6b3a764000085615bb3565b614ab19190615bd2565b60009586526014602090815260408088208489529091529095206001810195909555600654909455509192915050565b6008614aed838361561f565b1115614b0c57604051632902579f60e01b815260040160405180910390fd5b6040516301ffc9a760e01b80825233916301ffc9a791614b2e91600401615ca3565b602060405180830381865afa158015614b4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614b6f9190615c71565b1580614be357506040516301ffc9a760e01b815233906301ffc9a790614ba0906311686e4b60e21b90600401615ca3565b602060405180830381865afa158015614bbd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614be19190615c71565b155b15611f255760405163d03681f360e01b815260040160405180910390fd5b614c0d86338784613d13565b6000808415614c4757614c236127106064615bb3565b612710614c30878a615bb3565b614c3a9190615bb3565b614c449190615bd2565b91505b8515614c7e57614c5a6127106064615bb3565b612710614c67888a615bb3565b614c719190615bb3565b614c7b9190615bd2565b90505b8115614c8e57614c8e84836130c9565b8015614c9e57614c9e88826130c9565b6040516311686e4b60e21b815233906345a1b92c90614cc3908b908b90600401615c58565b600060405180830381600087803b158015614cdd57600080fd5b505af1158015614cf1573d6000803e3d6000fd5b505050505050505050505050565b600080826001811115614d1457614d1461567d565b1415614d37576000858152601f60205260409020600a01805460ff191660011790555b6001826001811115614d4b57614d4b61567d565b1415614d6e576000858152601f60205260409020600a01805460ff191660021790555b6000806000856080015165ffffffffffff16421115614daa57614da7866080015165ffffffffffff1642614da29190615b9c565b6153be565b91505b6000856001811115614dbe57614dbe61567d565b1415614ddd57614dda8660a00151876000015189601c5461416e565b90505b614dea6298968082615bd2565b8660400151614df9919061561f565b93506064614e078386615bb3565b614e119190615bd2565b9250614e1d8385615b9c565b93506000856001811115614e3357614e3361567d565b1415614e4d57601860008154614e4890615c20565b909155505b6001856001811115614e6157614e6161567d565b1415614e7b57601960008154614e7690615c20565b909155505b8215614e995782601b6000828254614e93919061561f565b90915550505b6000856001811115614ead57614ead61567d565b1415614ecf576000888152601f60205260409020600681018590556008018390555b81888a6001600160a01b03167fbd866a3fbf35e201f790e87581b1afbb3165e879df5d35313a4875a70b9f3b368787604051614f15929190918252602082015260400190565b60405180910390a450505095945050505050565b6001600160a01b03841660009081526015602090815260408083208684529091529020548214614f7a576001600160a01b038416600090815260156020908152604080832086845290915290208290555b6001600160a01b0384166000908152601560209081526040808320868452909152902060010154811461218e576001600160a01b0384166000908152601560209081526040808320868452909152902060010181905550505050565b6000614fe28233610a8d565b90506000198114611f25578061500b57604051632048203360e01b815260040160405180910390fd5b6001600160a01b0382166000908152603c6020526040812090335b6001600160a01b03166001600160a01b031681526020019081526020016000206000815461505390615cb8565b909155505050565b600061506782336109c8565b90506000198114611f25578061509057604051632048203360e01b815260040160405180910390fd5b6001600160a01b0382166000908152603a602052604081209033615026565b60006040518061018001604052808b81526020018a61ffff1681526020018981526020014265ffffffffffff168152602001620151808b6150f09190615bb3565b6150fa904261561f565b65ffffffffffff1681526020018881526020018781526020016000815260200184815260200160008152602001838152602001600060028111156151405761514061567d565b90526001600160a01b038c166000908152601d6020526040812080549293509091829061516c90615c20565b91829055506001600160a01b038d166000908152601e6020908152604080832084845282528083208a815560019081018a90558a8452601f8352928190208651815591860151828401805461ffff90921661ffff199092169190911790558501516002808301919091556060860151600383018054608089015165ffffffffffff908116600160301b026001600160601b031990921693169290921791909117905560a0860151600483015560c0860151600583015560e08601516006830155610100860151600783015561012086015160088301556101408601516009830155610160860151600a83018054959650879593949193909260ff199091169190849081111561527d5761527d61567d565b021790555090505084868d6001600160a01b03167f2b2d6cbbd64131cfb538b30ae4a9dc516d6fe02b65e223719e216846f0118a95856040516152c09190615adb565b60405180910390a4505050505050505050505050565b60008383106153515760006152eb8585615b9c565b905060006152fd620151806007615bb3565b9050808211615311576000925050506118df565b6153486201518061532c6153258486615b9c565b60016154d4565b6153369190615bd2565b61534190600161561f565b60636154eb565b925050506118df565b60028260048111156153655761536561567d565b1415615373575060006118df565b600261537f8686615b9c565b6153899190615bd2565b615393908661561f565b4210156153b3576040516354d335b760e11b815260040160405180910390fd5b506032949350505050565b60006153ce620151806007615bb3565b82116153dc57506000919050565b620151806153ec6007600161561f565b6153f69190615bb3565b821161540457506001919050565b620151806154146007600261561f565b61541e9190615bb3565b821161542c57506003919050565b6201518061543c6007600361561f565b6154469190615bb3565b821161545457506008919050565b620151806154646007600461561f565b61546e9190615bb3565b821161547c57506011919050565b6201518061548c6007600561561f565b6154969190615bb3565b82116154a457506023919050565b620151806154b46007600661561f565b6154be9190615bb3565b82116154cc57506048919050565b506063919050565b6000818311156154e55750816112aa565b50919050565b6000818311156154fc5750806112aa565b5090919050565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a081018290529060c08201905b905290565b6040518060600160405280600081526020016000815260200161553b615503565b60405180610180016040528060008152602001600061ffff16815260200160008152602001600065ffffffffffff168152602001600065ffffffffffff1681526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000600281111561553b5761553b61567d565b604051806080016040528060008152602001600081526020016000815260200161553b615561565b634e487b7160e01b600052601160045260246000fd5b6000821982111561563257615632615609565b500190565b80356001600160a01b038116811461564e57600080fd5b919050565b6000806040838503121561566657600080fd5b61566f83615637565b946020939093013593505050565b634e487b7160e01b600052602160045260246000fd5b600381106113d6576113d661567d565b805182526020810151602083015261ffff6040820151166040830152606081015165ffffffffffff8082166060850152806080840151166080850152505060a081015160a083015260c08101516156f981615693565b8060c0840152505050565b60e081016112aa82846156a3565b60006020828403121561572457600080fd5b81356001600160e01b03198116811461177057600080fd5b600060208083528351808285015260005b818110156157695785810183015185820160400152820161574d565b8181111561577b576000604083870101525b50601f01601f1916929092016040019392505050565b6000602082840312156157a357600080fd5b5035919050565b6000806000606084860312156157bf57600080fd5b505081359360208301359350604090920135919050565b600080604083850312156157e957600080fd5b6157f283615637565b915061580060208401615637565b90509250929050565b60008060006060848603121561581e57600080fd5b61582784615637565b925061583560208501615637565b9150604084013590509250925092565b60006020828403121561585757600080fd5b61177082615637565b600080600080600060a0868803121561587857600080fd5b61588186615637565b94506020860135935060408601359250606086013591506158a460808701615637565b90509295509295909350565b600080604083850312156158c357600080fd5b50508035926020909101359150565b600080600080608085870312156158e857600080fd5b84359350602085013592506040850135915061590660608601615637565b905092959194509250565b6001600160a01b0391909116815260200190565b602080825282518282018190526000919060409081850190868401855b8281101561597c578151805185528681015187860152850151615967868601826156a3565b50610120939093019290850190600101615942565b5091979650505050505050565b602081016002831061599d5761599d61567d565b91905290565b600080600080608085870312156159b957600080fd5b6159c285615637565b966020860135965060408601359560600135945092505050565b80151581146113d657600080fd5b600080604083850312156159fd57600080fd5b615a0683615637565b91506020830135615a16816159dc565b809150509250929050565b615a2a81615693565b9052565b805182526020810151615a47602084018261ffff169052565b50604081015160408301526060810151615a6b606084018265ffffffffffff169052565b506080810151615a85608084018265ffffffffffff169052565b5060a081015160a083015260c081015160c083015260e081015160e08301526101008082015181840152506101208082015181840152506101408082015181840152506101608082015161218e82850182615a21565b61018081016112aa8284615a2e565b602080825282518282018190526000919060409081850190868401855b8281101561597c578151805185528681015187860152858101518686015260609081015190615b3881870183615a2e565b50506101e0939093019290850190600101615b07565b600181811c90821680615b6257607f821691505b602082108114156154e557634e487b7160e01b600052602260045260246000fd5b600060208284031215615b9557600080fd5b5051919050565b600082821015615bae57615bae615609565b500390565b6000816000190483118215151615615bcd57615bcd615609565b500290565b600082615bef57634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000600019821415615c3457615c34615609565b5060010190565b82815260408101615c4b83615693565b8260208301529392505050565b6001600160a01b03929092168252602082015260400190565b600060208284031215615c8357600080fd5b8151611770816159dc565b828152610100810161177060208301846156a3565b6001600160e01b031991909116815260200190565b600081615cc757615cc7615609565b50600019019056fec4f8f7f5ee45326dd80cc2262cf4948c0aa62c4ed9b775cbe9662ca3618994d3ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220e147f8db36d790d87b37772e1baf6c0932f5345d8bcbb31daca1afd7a334e2c864736f6c634300080a0033

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

0000000000000000000000006c3c3a22ab3725c017d41d31b23efaf51a8ece480000000000000000000000009bff9f810d19cdb4bf7701c9d5ad101e91cda08d000000000000000000000000f19308f923582a6f7c465e5ce7a9dc1bec6665b1000000000000000000000000a2d21205aa7273baddfc8e9551e05e23bb49ce4600000000000000000000000015e5b9b9adf208cc7ca3ae1e6a49506eb5f397dd

-----Decoded View---------------
Arg [0] : genesisAddress (address): 0x6c3C3A22ab3725C017d41d31B23EfaF51A8ecE48
Arg [1] : buyAndBurnAddress (address): 0x9Bff9F810D19cDb4BF7701C9d5aD101E91CdA08d
Arg [2] : titanxAddress (address): 0xF19308F923582A6f7c465e5CE7a9Dc1BEC6665B1
Arg [3] : treasuryAddress (address): 0xA2d21205Aa7273BadDFC8E9551e05E23bB49ce46
Arg [4] : investmentAddress (address): 0x15E5B9B9Adf208cC7CA3aE1e6a49506eB5f397Dd

-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 0000000000000000000000006c3c3a22ab3725c017d41d31b23efaf51a8ece48
Arg [1] : 0000000000000000000000009bff9f810d19cdb4bf7701c9d5ad101e91cda08d
Arg [2] : 000000000000000000000000f19308f923582a6f7c465e5ce7a9dc1bec6665b1
Arg [3] : 000000000000000000000000a2d21205aa7273baddfc8e9551e05e23bb49ce46
Arg [4] : 00000000000000000000000015e5b9b9adf208cc7ca3ae1e6a49506eb5f397dd


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

A revolutionary crypto token on ETH blockchain. PERPETUAL burns aren't solely dependent on miners.

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.