账户
0x26...2edf
HELIOS

HELIOS

$500
此合同的源代码已经过验证!
合同元数据
编译器
0.8.10+commit.fc410830
语言
Solidity
合同源代码
文件 1 的 19: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];
    }
}
合同源代码
文件 2 的 19: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;
    }
}
合同源代码
文件 3 的 19: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 {}
}
合同源代码
文件 4 的 19: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];
    }
}
合同源代码
文件 5 的 19: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;
    }
}
合同源代码
文件 6 的 19:IBuynBurn.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

interface IBuynBurn {
    function getCurrentTitanPrice() external view returns (uint256);
}
合同源代码
文件 7 的 19: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);
}
合同源代码
文件 8 的 19: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);
}
合同源代码
文件 9 的 19: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);
}
合同源代码
文件 10 的 19:IHlxOnBurn.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

interface IHlxOnBurn {
    function onBurn(address user, uint256 amount) external;
}
合同源代码
文件 11 的 19: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;
}
合同源代码
文件 12 的 19:ITitanOnBurn.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

interface ITitanOnBurn {
    function onBurn(address user, uint256 amount) external;
}
合同源代码
文件 13 的 19: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;
    }
}
合同源代码
文件 14 的 19: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;
    }
}
合同源代码
文件 15 的 19: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;
    }
}
合同源代码
文件 16 的 19: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;
    }
}
合同源代码
文件 17 的 19: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;
}
合同源代码
文件 18 的 19: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;
合同源代码
文件 19 的 19: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
}
设置
{
  "compilationTarget": {
    "contracts/Helios.sol": "HELIOS"
  },
  "evmVersion": "london",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": true,
    "runs": 0
  },
  "remappings": []
}
ABI
[{"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"}]