EthereumEthereum
0x96...1862
DragonX

DragonX

DRAGONX

Token
Market Cap
$1.00
 
Price
2%
This contract's source code is verified!
Contract Metadata
Compiler
0.8.20+commit.a1b79de6
Language
Solidity
Contract Source Code
File 1 of 17: Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}
Contract Source Code
File 2 of 17: Constants.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.20;

/* Common */
uint256 constant BASIS = 10_000;
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;

/* TitanX staking */
uint256 constant TITANX_MAX_STAKE_PER_WALLET = 1000;
uint256 constant TITANX_MIN_STAKE_LENGTH = 28;
uint256 constant TITANX_MAX_STAKE_LENGTH = 3500;

/* TitanX Stake Longer Pays Better bonus */
uint256 constant TITANX_LPB_MAX_DAYS = 2888;
uint256 constant TITANX_LPB_PER_PERCENT = 825;

uint256 constant TITANX_BPB_MAX_TITAN = 100 * 1e9 * SCALING_FACTOR_1e18; //100 billion
uint256 constant TITANX_BPB_PER_PERCENT = 1_250_000_000_000 *
    SCALING_FACTOR_1e18;

/* Addresses */
address constant TITANX_ADDRESS = 0xF19308F923582A6f7c465e5CE7a9Dc1BEC6665B1;
address constant WETH9_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address constant UNI_SWAP_ROUTER = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
address constant UNI_FACTORY = 0x1F98431c8aD98523631AE4a59f267346ea31F984;
address constant UNI_NONFUNGIBLEPOSITIONMANAGER = 0xC36442b4a4522E871399CD717aBDD847Ab11FE88;

/* Uniswap Liquidity Pools (DragonX, TitanX) */
uint24 constant FEE_TIER = 10000;
int24 constant MIN_TICK = -887200;
int24 constant MAX_TICK = 887200;
uint160 constant INITIAL_SQRT_PRICE_TITANX_DRAGONX = 79228162514264337593543950336; // 1:1

/* DragonX Constants */
uint256 constant INCENTIVE_FEE = 300;

Contract Source Code
File 3 of 17: Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

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

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

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
Contract Source Code
File 4 of 17: Create2.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Create2.sol)

pragma solidity ^0.8.20;

/**
 * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
 * `CREATE2` can be used to compute in advance the address where a smart
 * contract will be deployed, which allows for interesting new mechanisms known
 * as 'counterfactual interactions'.
 *
 * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
 * information.
 */
library Create2 {
    /**
     * @dev Not enough balance for performing a CREATE2 deploy.
     */
    error Create2InsufficientBalance(uint256 balance, uint256 needed);

    /**
     * @dev There's no code to deploy.
     */
    error Create2EmptyBytecode();

    /**
     * @dev The deployment failed.
     */
    error Create2FailedDeployment();

    /**
     * @dev Deploys a contract using `CREATE2`. The address where the contract
     * will be deployed can be known in advance via {computeAddress}.
     *
     * The bytecode for a contract can be obtained from Solidity with
     * `type(contractName).creationCode`.
     *
     * Requirements:
     *
     * - `bytecode` must not be empty.
     * - `salt` must have not been used for `bytecode` already.
     * - the factory must have a balance of at least `amount`.
     * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
     */
    function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) {
        if (address(this).balance < amount) {
            revert Create2InsufficientBalance(address(this).balance, amount);
        }
        if (bytecode.length == 0) {
            revert Create2EmptyBytecode();
        }
        /// @solidity memory-safe-assembly
        assembly {
            addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
        }
        if (addr == address(0)) {
            revert Create2FailedDeployment();
        }
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
     * `bytecodeHash` or `salt` will result in a new destination address.
     */
    function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
        return computeAddress(salt, bytecodeHash, address(this));
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
     * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
     */
    function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40) // Get free memory pointer

            // |                   | ↓ ptr ...  ↓ ptr + 0x0B (start) ...  ↓ ptr + 0x20 ...  ↓ ptr + 0x40 ...   |
            // |-------------------|---------------------------------------------------------------------------|
            // | bytecodeHash      |                                                        CCCCCCCCCCCCC...CC |
            // | salt              |                                      BBBBBBBBBBBBB...BB                   |
            // | deployer          | 000000...0000AAAAAAAAAAAAAAAAAAA...AA                                     |
            // | 0xFF              |            FF                                                             |
            // |-------------------|---------------------------------------------------------------------------|
            // | memory            | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
            // | keccak(start, 85) |            ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |

            mstore(add(ptr, 0x40), bytecodeHash)
            mstore(add(ptr, 0x20), salt)
            mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
            let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
            mstore8(start, 0xff)
            addr := keccak256(start, 85)
        }
    }
}
Contract Source Code
File 5 of 17: DragonStake.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.20;

// OpenZeppelin
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

// Library
import "./interfaces/ITitanX.sol";
import "../DragonX.sol";
import "./Constants.sol";

/**
 * @title A contract managed and deployed by DragonX to initialise the maximum amount of stakes per address
 * @author The DragonX devs
 * @notice This contract is instantiated by DragonX and will not be deployed as a separate entity
 */
contract DragonStake is Ownable {
    using SafeERC20 for IERC20;
    using SafeERC20 for ITitanX;

    // -----------------------------------------
    // Type declarations
    // -----------------------------------------

    // -----------------------------------------
    // State variables
    // -----------------------------------------
    uint256 public openedStakes;

    // -----------------------------------------
    // Errors
    // -----------------------------------------
    /**
     * @dev Error emitted when a user tries to end a stake but is not mature yet.
     */
    error StakeNotMature();

    // -----------------------------------------
    // Events
    // -----------------------------------------

    // -----------------------------------------
    // Modifiers
    // -----------------------------------------

    // -----------------------------------------
    // Constructor
    // -----------------------------------------
    constructor() Ownable(msg.sender) {}

    // -----------------------------------------
    // Receive function
    // -----------------------------------------
    /**
     * @dev Receive function to handle plain Ether transfers.
     * Reverts if the sender is not the allowed address.
     */
    receive() external payable {
        require(msg.sender == TITANX_ADDRESS, "Sender not authorized");
    }

    // -----------------------------------------
    // Fallback function
    // -----------------------------------------
    /**
     * @dev Fallback function to handle non-function calls or Ether transfers if receive() doesn't exist.
     * Reverts if the sender is not the allowed address.
     */
    fallback() external payable {
        revert("Fallback triggered");
    }

    // -----------------------------------------
    // External functions
    // -----------------------------------------
    /**
     * TitanX Staking Function
     * @notice Stakes all available TitanX tokens held by this contract.
     * @dev Initializes the TitanX contract, calculates the stakable balance, and opens a new stake.
     *      This function can only be called by the contract owner.
     */
    function stake() external onlyOwner {
        // Initialize TitanX contract reference
        ITitanX titanX = ITitanX(TITANX_ADDRESS);

        // Fetch the current balance of TitanX tokens in this contract
        uint256 amountToStake = titanX.balanceOf(address(this));

        // Initiate staking of the fetched amount for the maximum defined stake length
        titanX.startStake(amountToStake, TITANX_MAX_STAKE_LENGTH);

        // Increment the count of active stakes
        openedStakes += 1;
    }

    /**
     * Claim ETH Rewards from TitanX Staking
     * @notice Allows the contract owner to claim accumulated ETH rewards from TitanX staking.
     * @dev Retrieves the total claimable ETH amount and, if any, claims it and sends it to the owner's address.
     *      This function can only be called by the contract owner.
     * @return claimable The total amount of ETH claimed.
     */
    function claim() external onlyOwner returns (uint256 claimable) {
        // Initialize TitanX contract reference
        ITitanX titanX = ITitanX(TITANX_ADDRESS);

        // Determine the total amount of ETH that can be claimed by this contract
        claimable = titanX.getUserETHClaimableTotal(address(this));

        // Proceed with claiming if there is any claimable ETH
        if (claimable > 0) {
            // Claim the available ETH from TitanX
            titanX.claimUserAvailableETHPayouts();

            // Transfer the claimed ETH to the contract owner
            Address.sendValue(payable(owner()), claimable);
        }
    }

    /**
     * @dev Ends a stake after it has reached its maturity.
     *
     * This function interacts with the ITitanX contract to handle stake operations.
     * It requires the stake ID (sId) to be valid and within the range of opened stakes.
     * If the current block timestamp is greater than or equal to the stake's maturity timestamp,
     * the function ends the stake and transfers the unstaked TitanX tokens to the DragonX contract.
     * If the stake has not yet matured, the function will revert.
     *
     * @param sId The ID of the stake to be ended.
     * @notice The function is callable externally and interacts with ITitanX and IERC20 contracts.
     * @notice It is required that the stake ID is valid and the stake is matured.
     * @notice The function will revert if the stake is not matured.
     */
    function endStakeAfterMaturity(uint256 sId) external {
        ITitanX titanX = ITitanX(TITANX_ADDRESS);
        require(sId > 0 && sId <= openedStakes, "invalid ID");

        UserStakeInfo memory stakeInfo = titanX.getUserStakeInfo(
            address(this),
            sId
        );

        // End stake if matured
        if (block.timestamp >= stakeInfo.maturityTs) {
            // track TitanX balance
            uint256 before = titanX.balanceOf(address(this));

            // End the stake
            titanX.endStake(sId);

            // Send total amount unstaked back to DragonX
            uint256 unstaked = titanX.balanceOf(address(this)) - before;

            // Transfer TitanX to DragonX
            IERC20(TITANX_ADDRESS).safeTransfer(owner(), unstaked);

            // Update DragonX
            DragonX(payable(owner())).stakeEnded(unstaked);
        } else {
            revert StakeNotMature();
        }
    }

    /**
     * Send TitanX Balance to DragonX
     *
     * @dev This function transfers any TitanX tokens held by this contract to the owner,
     * representing the DragonX account. This is a safety mechanism to handle
     * rare situations where TitanX tokens are accidentally sent to this contract or are
     * left over from operations like calling `TitanX#endStakeForOthers`.
     *
     * It's important to note that this function could lead to slight discrepancies in
     * DragonX's accounting, specifically in the `totalTitanUnstaked` value - there is 
     * no way to distinguish between TitanX send to this contract by accident or
     * users calling `TitanX#endStakeForOthers`.
     *
     * @notice Use this function to transfer TitanX tokens from the contract to the DragonX
     * owner address in case of accidental transfers or calling `TitanX#endStakeForOthers`
     */
    function sendTitanX() external {
        IERC20 titanX = IERC20(TITANX_ADDRESS);

        // transfer
        titanX.safeTransfer(owner(), titanX.balanceOf(address(this)));

        // update the vault
        DragonX(payable(owner())).updateVault();
    }

    /**
     * @dev Calculates the total amount of Ethereum claimable by the contract.
     *      Calls `getUserETHClaimableTotal` from the TitanX contract to retrieve the total claimable amount.
     * @return claimable The total amount of Ethereum claimable by the contract.
     */
    function totalEthClaimable() external view returns (uint256 claimable) {
        // Initialize TitanX contract reference
        ITitanX titanX = ITitanX(TITANX_ADDRESS);

        claimable = titanX.getUserETHClaimableTotal(address(this));
    }

    /**
     * @dev Determines whether any of the stakes have reached their maturity date.
     *      Iterates through all user stakes and checks if the current block timestamp
     *      is at or past the stake's maturity timestamp.
     * @return A boolean indicating whether at least one stake has reached maturity.
     */
    function stakeReachedMaturity() external view returns (bool, uint256) {
        ITitanX titanX = ITitanX(TITANX_ADDRESS);
        UserStake[] memory stakes = titanX.getUserStakes(address(this));

        for (uint256 idx; idx < stakes.length; idx++) {
            if (block.timestamp > stakes[idx].stakeInfo.maturityTs) {
                return (true, stakes[idx].sId);
            }
        }

        return (false, 0);
    }

    // -----------------------------------------
    // Public functions
    // -----------------------------------------

    // -----------------------------------------
    // Internal functions
    // -----------------------------------------

    // -----------------------------------------
    // Private functions
    // -----------------------------------------
}
Contract Source Code
File 6 of 17: DragonX.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.20;

// OpenZeppelin
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/Create2.sol";
import "@openzeppelin/contracts/access/Ownable2Step.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

// Library
import "./lib/DragonStake.sol";
import "./lib/Constants.sol";
import "./lib/Types.sol";
import "./lib/interfaces/ITitanX.sol";

/**
 * @title The DragonX Contranct
 * @author The DragonX devs
 */
contract DragonX is ERC20, Ownable2Step, ReentrancyGuard {
    using SafeERC20 for IERC20;
    using SafeERC20 for ITitanX;

    // -----------------------------------------
    // Type declarations
    // -----------------------------------------

    // -----------------------------------------
    // State variables
    // -----------------------------------------
    /**
     * @notice The TitanX buy contract address.
     * Set at runtime, this address allows for upgrading the buy contract version.
     */
    address public titanBuyAddress;

    /**
     * @notice The DragonX buy and burn contract address.
     * Set at runtime, this allows for upgrading the DragonX buy and burn contract.
     */
    address public dragonBuyAndBurnAddress;

    /**
     * @notice The start time of the mint phase, expressed in UTC seconds.
     * Indicates when the minting phase for tokens begins.
     */
    uint256 public mintPhaseBegin;

    /**
     * @notice The end time of the mint phase, expressed in UTC seconds.
     * Indicates when the minting phase for tokens ends.
     */
    uint256 public mintPhaseEnd;

    /**
     * @notice mint ratios from launch for 84 days (12 weeks)
     */
    uint256 public constant mintRatioWeekOne = BASIS;
    uint256 public constant mintRatioWeekTwo = BASIS;
    uint256 public constant mintRatioWeekThree = 9500;
    uint256 public constant mintRatioWeekFour = 9000;
    uint256 public constant mintRatioWeekFive = 8500;
    uint256 public constant mintRatioWeekSix = 8000;
    uint256 public constant mintRatioWeekSeven = 7500;
    uint256 public constant mintRatioWeekEight = 7000;
    uint256 public constant mintRatioWeekNine = 6500;
    uint256 public constant mintRatioWeekTen = 6000;
    uint256 public constant mintRatioWeekEleven = 5500;
    uint256 public constant mintRatioWeekTwelve = 5000;

    /**
     * @notice The time when it's possible to open a new TitanX stake after the cooldown.
     * This cooldown period controls the frequency of new stakes being initiated.
     */
    uint256 public nextStakeTs;

    /**
     * @notice The number of DragonStake contracts that have been deployed.
     * Tracks how many DragonStake contracts exist within the system.
     */
    uint256 public numDragonStakeContracts;

    /**
     * @notice The address of the currently active DragonStake contract instance.
     * This contract is used for initiating new TitanX stakes.
     */
    address public activeDragonStakeContract;

    /**
     * @notice A mapping from an ID to a deployed instance of the DragonStake contract.
     * The index starts at zero. Use a loop to iterate through instances, e.g., for(uint256 idx = 0; idx < numDragonStakeContracts; idx++).
     */
    mapping(uint256 => address) public dragonStakeContracts;

    /**
     * @notice The amount of TitanX currently held by this contract and not used in stakes.
     * Represents the reserve of TitanX tokens that are available but not currently staked.
     */
    uint256 public vault;

    /**
     * @notice The total amount of Titan staked by DragonX
     */
    uint256 public totalTitanStaked;

    /**
     * @notice The total amount of Titan unstaked by DragonX
     */
    uint256 public totalTitanUnstaked;

    /**
     * @notice The total amount of ETH claimed by DragonX
     */
    uint256 public totalEthClaimed;

    /**
     * Indicates that the initial liquidity has been minted
     */
    InitialLiquidityMinted public initalLiquidityMinted;

    /**
     * @dev Mapping of amounts allocated to the genesis address held by this contract.
     * - address(0): Represents the ETH allocated.
     * - address(TitanX): Represents the TitanX tokens allocated.
     * - address(this): Represents the DragonX tokens allocated.
     */
    mapping(address => uint256) private _genesisVault;

    /**
     * @dev Mapping of address to bool indicating if an address is allowed to send ETH
     * to DragonX limiting EOA addresses from accidently sending ETH to DragonX
     */
    mapping(address => bool) private _receiveEthAllowlist;

    /**
     * @dev Mapping of address to bool indicating if an address is a DragonStake instance
     */
    mapping(address => bool) private _dragonStakeAllowlist;

    // -----------------------------------------
    // Events
    // -----------------------------------------
    /**
     * @dev Event emitted when a new Dragon stake instance is created.
     * @param stakeContractId Unique identifier of the stake contract.
     * @param stakeContractAddress Address of the newly created stake contract.
     */
    event DragonStakeInstanceCreated(
        uint256 indexed stakeContractId,
        address indexed stakeContractAddress
    );

    /**
     * @notice Emitted when staking rewards are claimed.
     * @param caller The address of the caller who initiated the transaction.
     * @param totalClaimed The total amount of ETH claimed.
     * @param titanBuy Amount transfered to TitanBuy.
     * @param dragonBuyAndBurn Amount transfered to DragonBuyAndBurn
     * @param genesis Amount accounted to genesis
     * @param incentiveFee Incentive see send to caller
     * (this might include the incentice for calling triggerPayouts on TitanX)
     */
    event Claimed(
        address indexed caller,
        uint256 indexed totalClaimed,
        uint256 titanBuy,
        uint256 dragonBuyAndBurn,
        uint256 genesis,
        uint256 incentiveFee
    );

    /**
     * @notice Emitted when a new TitanX stake is opened by Dragonx
     * @param dragonStakeAddress The DragonStake instance used for this stake
     * @param amount The amount staked
     */
    event TitanStakeStarted(address indexed dragonStakeAddress, uint256 amount);

    /**
     * @notice Emitted when TitanX stakes are ended by Dragonx
     * @param dragonStakeAddress The DragonStake instance used for this action
     * @param amount The amount unstaked
     */
    event TitanStakesEnded(address indexed dragonStakeAddress, uint256 amount);

    // -----------------------------------------
    // Errors
    // -----------------------------------------
    /**
     * @dev Error emitted when a user tries to mint but the minting phase has not started.
     * This prevents actions related to minting before the official commencement of the minting period.
     */
    error MintingNotYetActive();

    /**
     * @dev Error when a user tries to mint but the minting phase has ended.
     * This ensures minting operations are restricted to the designated minting timeframe.
     */
    error MintingPeriodOver();

    /**
     * @dev Emitted when a user tries to mint but the TitanX allowance for this contract is too low.
     * Indicates that the contract does not have enough TitanX tokens allocated to it for the minting operation.
     */
    error InsufficientTitanXAllowance();

    /**
     * @dev Emitted when a user tries to mint without having enough TitanX tokens.
     * This ensures that users have a sufficient balance of TitanX tokens to perform the minting operation.
     */
    error InsufficientTitanXBalance();

    /**
     * @dev Error emitted when the stake function is currently in the cooldown period and cannot be called.
     * This enforces a waiting period before the stake function can be executed again.
     */
    error CooldownPeriodActive();

    /**
     * @dev Emitted when no additional stakes can be opened.
     * This is triggered when the maximum limit of open stakes is reached.
     */
    error NoAdditionalStakesAllowed();

    /**
     * @dev Error emitted when there is no ETH claimable by the function caller.
     * This ensures that the claim operation is only performed when there is ETH available to be claimed.
     */
    error NoEthClaimable();

    /**
     * @dev Error emitted when there are no tokens available to stake.
     * This ensures that the staking operation is only executed when there are tokens to be staked.
     */
    error NoTokensToStake();

    /**
     * @dev Error emitted when there is no need for creating a new Dragon stake instance.
     * This occurs when attempting to create a redundant Dragon stake instance.
     */
    error NoNeedForNewDragonStakeInstance();

    /**
     * @dev Error emitted when an invalid address is given to a function.
     * This occurs when the genesis address manages an address and passes address(0) by accident.
     */
    error InvalidAddress();

    /**
     * @dev Error emitted when a user attempts to mint but the initial liquidity has net yet been mined
     */
    error LiquidityNotMintedYet();

    /**
     * @dev Thrown when the function caller is not authorized or expected.
     */
    error InvalidCaller();

    // -----------------------------------------
    // Modifiers
    // -----------------------------------------
    /**
     * @dev Modifier to restrict function access to allowed DragonStake contracts.
     *
     * This modifier ensures that the function can only be called by addresses that are
     * present in the `_dragonStakeAllowlist`. If the calling address is not on the allowlist,
     * the transaction will revert with the message "not allowed".
     * @notice Use this modifier to restrict function access to specific addresses only.
     */
    modifier onlyDragonStake() {
        require(_dragonStakeAllowlist[_msgSender()], "not allowed");
        _;
    }

    // -----------------------------------------
    // Constructor
    // -----------------------------------------
    /**
     * @notice Constructor for the DragonX ERC20 Token Contract.
     * @dev Initializes the contract, sets up the minting phase, and deploys the first DragonStake instance.
     *      - Inherits from ERC20 and sets token name to "DragonX" and symbol to "DRAGONX".
     *      - Calculates and sets the start and end times for the minting phase based on current time.
     *      - Sets the time for the next restake opportunity.
     *      - Deploys the first DragonStake contract instance.
     *      - Transfers ownership to contract deployer.
     *      - Set the initial TitanBuy and DragonBuyAndBurn contract addresses.
     * @param titanBuyAddress_ The address of the TitanBuy contract instance.
     * @param dragonBuyAndBurnAdddress_ The address of the DragonBuyAndBurn contract instance.
     */
    constructor(
        address titanBuyAddress_,
        address dragonBuyAndBurnAdddress_
    ) ERC20("DragonX", "DRAGONX") Ownable(msg.sender) {
        if (titanBuyAddress_ == address(0)) {
            revert InvalidAddress();
        }
        if (dragonBuyAndBurnAdddress_ == address(0)) {
            revert InvalidAddress();
        }

        // Deploy stake contract instance setting DragonX as its owner
        _deployDragonStakeInstance();

        // Set contract addresses
        titanBuyAddress = titanBuyAddress_;
        dragonBuyAndBurnAddress = dragonBuyAndBurnAdddress_;

        // set other states
        initalLiquidityMinted = InitialLiquidityMinted.No;

        // Allow TitanX to send ETH to DragonX (incentive fee)
        _receiveEthAllowlist[TITANX_ADDRESS] = true;
    }

    // -----------------------------------------
    // Receive function
    // -----------------------------------------
    /**
     * @dev Receive function to handle plain Ether transfers.
     * Reverts if the sender is not one of the DragonStake contracts.
     */
    receive() external payable {
        require(_receiveEthAllowlist[msg.sender], "Sender not authorized");
    }

    // -----------------------------------------
    // Fallback function
    // -----------------------------------------
    /**
     * @dev Fallback function to handle non-function calls or Ether transfers if receive() doesn't exist.
     * Always revert
     */
    fallback() external {
        revert("Fallback triggered");
    }

    // -----------------------------------------
    // External functions
    // -----------------------------------------
    /**
     * This function enables the minting of DragonX tokens in exchange for TitanX.
     * Users can transfer TitanX to the DragonX contract to mint an equivalent amount of DragonX tokens.
     * The minting process is available only during a specified time frame.
     * When minting, 8% of the total minted DragonX supply and 8% of the TitanX used for minting
     * are allocated to the genesis address. The remaining TitanX is retained within the contract.
     * Minting starts once the initial liquidity has been minted (indicating all other contracts)
     * have been deployed and initialized successfully by the genesis address.
     * @param amount The amount of DragonX tokens to be minted.
     */
    function mint(uint256 amount) external {
        // Cache state variables
        uint256 mintPhaseBegin_ = mintPhaseBegin;

        // To avoid being frontrun, minting creating DragonX tokens will only
        // be able once the inital liqudiity ahs been created
        if (initalLiquidityMinted != InitialLiquidityMinted.Yes) {
            revert LiquidityNotMintedYet();
        }

        // Check if the minting phase is currently active
        if (block.timestamp < mintPhaseBegin_) {
            revert MintingNotYetActive();
        }

        if (block.timestamp > mintPhaseEnd) {
            revert MintingPeriodOver();
        }

        ITitanX titanX = ITitanX(TITANX_ADDRESS);
        // Ensure the user has sufficient TitanX and has granted enough allowance
        if (titanX.allowance(_msgSender(), address(this)) < amount) {
            revert InsufficientTitanXAllowance();
        }
        if (titanX.balanceOf(_msgSender()) < amount) {
            revert InsufficientTitanXBalance();
        }

        // Transfer TitanX from the user to this contract
        titanX.safeTransferFrom(_msgSender(), address(this), amount);

        uint256 ratio;
        if (block.timestamp < mintPhaseBegin_ + 7 days) {
            // week 1
            ratio = mintRatioWeekOne;
        } else if (block.timestamp < mintPhaseBegin_ + 14 days) {
            // week 2
            ratio = mintRatioWeekTwo;
        } else if (block.timestamp < mintPhaseBegin_ + 21 days) {
            // week 3
            ratio = mintRatioWeekThree;
        } else if (block.timestamp < mintPhaseBegin_ + 28 days) {
            // week 4
            ratio = mintRatioWeekFour;
        } else if (block.timestamp < mintPhaseBegin_ + 35 days) {
            // week 5
            ratio = mintRatioWeekFive;
        } else if (block.timestamp < mintPhaseBegin_ + 42 days) {
            // week 6
            ratio = mintRatioWeekSix;
        } else if (block.timestamp < mintPhaseBegin_ + 49 days) {
            // week 7
            ratio = mintRatioWeekSeven;
        } else if (block.timestamp < mintPhaseBegin_ + 56 days) {
            // week 8
            ratio = mintRatioWeekEight;
        } else if (block.timestamp < mintPhaseBegin_ + 63 days) {
            // weeek 9
            ratio = mintRatioWeekNine;
        } else if (block.timestamp < mintPhaseBegin_ + 70 days) {
            // week 10
            ratio = mintRatioWeekTen;
        } else if (block.timestamp < mintPhaseBegin_ + 77 days) {
            // week 11
            ratio = mintRatioWeekEleven;
        } else {
            // week 12
            ratio = mintRatioWeekTwelve;
        }

        // calculate the amount to mint
        uint256 mintAmount = (amount * ratio) / BASIS;

        // Mint an equivalent amount of DragonX tokens
        _mint(_msgSender(), mintAmount);

        // Calculate and mint the genesis 8% share (of total supply minted)
        uint256 dragonGenesisShare = (mintAmount * 800) / BASIS;
        _mint(address(this), dragonGenesisShare);

        // Allocate 8% of DragonX to the genesis vault
        _genesisVault[address(this)] += dragonGenesisShare;

        // Allocate 8% of total TitanX send to DragonX to genesis vault
        uint256 titanGenesisShare = (amount * 800) / BASIS;
        _genesisVault[address(titanX)] += titanGenesisShare;

        // Retain the remaining TitanX within the contract's vault
        vault += amount - titanGenesisShare;
    }

    /**
     * This function allows users to open a new TitanX stake through the DragonX contract.
     * Each stake runs for the maximum duration, and upon completion, the TitanX is burned.
     *
     * A stake can be opened when either of the following conditions is met:
     * 1. The vault has sufficient TitanX tokens to achieve the maximum 'bigger pays better' bonus.
     * 2. If the vault doesn't have enough tokens, the function can be invoked after a cooldown
     *    period of 1 week. This delay allows the accumulation of sufficient TitanX to gain
     *    the 'bigger pays better' bonus.
     */
    function stake() external {
        DragonStake dragonStake = DragonStake(
            payable(activeDragonStakeContract)
        );

        if (dragonStake.openedStakes() >= TITANX_MAX_STAKE_PER_WALLET) {
            revert NoAdditionalStakesAllowed();
        }

        updateVault();

        // Cache state variables
        uint256 vault_ = vault;

        if (vault_ == 0) {
            revert NoTokensToStake();
        }

        if (vault_ >= TITANX_BPB_MAX_TITAN) {
            // Start a stake using the currently active DragonStake instance
            _startStake();

            // Schedule the next possible stake after a 7-day cooldown period
            nextStakeTs = block.timestamp + 7 days;
        } else {
            // If the vault lacks sufficient TitanX, a stake can be opened only
            // after a cooldown period of 7 days to allow for token accumulation.
            if (block.timestamp < nextStakeTs) {
                revert CooldownPeriodActive();
            }

            // Start a new stake using the currently active DragonStake instance
            _startStake();

            // Schedule the next possible stake after a 7-day cooldown period.
            nextStakeTs = block.timestamp + 7 days;
        }
    }

    /**
     * Claim Function for ETH Rewards
     * This function claims ETH rewards based on TitanX stakes and allocates them according to predefined shares.
     * @dev The function performs the following operations:
     *      1. Retrieves the claimable ETH amount from TitanX stakes.
     *      2. Validates if there is any ETH to claim, and reverts if none is available.
     *      3. Claims the available ETH payouts.
     *      4. Calculates and distributes the ETH according to predefined shares:
     *         - 8% is allocated as a genesis share.
     *         - 3% is sent as a tip to the caller of the function.
     *         - 44.5% is used for buying and burning DragonX tokens.
     *         - The remaining 44.5% is used for buying and burning TitanX tokens.
     *      5. Updates the respective vaults with their allocated shares.
     *      6. Sends the tip to the caller of the function.
     */
    function claim() external nonReentrant returns (uint256 claimedAmount) {
        //prevent contract accounts (bots) from calling this function
        if (msg.sender != tx.origin) {
            revert InvalidCaller();
        }

        // Trigger payouts on TitanX
        // This potentially sends an incentive fee to DragonX
        // The incentive fee is transparently forwarded to the caller
        uint256 ethBalanceBefore = address(this).balance;
        ITitanX(TITANX_ADDRESS).triggerPayouts();
        uint256 triggerPayoutsIncentiveFee = address(this).balance -
            ethBalanceBefore;

        // Retrieve the total claimable ETH amount.
        for (uint256 idx; idx < numDragonStakeContracts; idx++) {
            DragonStake dragonStake = DragonStake(
                payable(dragonStakeContracts[idx])
            );
            claimedAmount += dragonStake.claim();
        }

        // Check if there is any claimable ETH, revert if none.
        if (claimedAmount == 0) {
            revert NoEthClaimable();
        }

        // Calculate the genesis share (8%).
        uint256 genesisShare = (claimedAmount * 800) / BASIS;

        // Calculate the tip for the caller (3%).
        uint256 incentiveFee = (claimedAmount * INCENTIVE_FEE) / BASIS;

        // Calculate the Buy and Burn share for DragonX (44.5%).
        uint256 buyAndBurnDragonX = (claimedAmount * 4450) / BASIS;

        // Calculate the Buy and Burn share for TitanX (remainder, ~44.5%).
        uint256 buyTitanX = claimedAmount -
            genesisShare -
            buyAndBurnDragonX -
            incentiveFee;

        // Update the genesis vault with the genesis share.
        _genesisVault[address(0)] += genesisShare;

        // Send to the Buy and Burn contract for DragonX.
        Address.sendValue(payable(dragonBuyAndBurnAddress), buyAndBurnDragonX);

        // Send to the buy contract for TitanX.
        Address.sendValue(payable(titanBuyAddress), buyTitanX);

        // Send the tip to the function caller.
        address sender = _msgSender();
        Address.sendValue(
            payable(sender),
            incentiveFee + triggerPayoutsIncentiveFee
        );

        // update state
        totalEthClaimed += claimedAmount;

        // Emit event
        emit Claimed(
            sender,
            claimedAmount,
            buyTitanX,
            buyAndBurnDragonX,
            genesisShare,
            incentiveFee + triggerPayoutsIncentiveFee
        );
    }

    /**
     * @notice Factory function to deploy a new DragonStake contract instance.
     * @dev This function deploys a new DragonStake instance if the number of open stakes in the current
     *      active instance exceeds the maximum allowed per wallet.
     *      It reverts with NoNeedForNewDragonStakeInstance if the condition is not met.
     *      Only callable externally.
     */
    function deployNewDragonStakeInstance() external {
        DragonStake dragonStake = DragonStake(
            payable(activeDragonStakeContract)
        );

        // Check if the maximum number of stakes per wallet has been reached
        if (dragonStake.openedStakes() < TITANX_MAX_STAKE_PER_WALLET) {
            revert NoNeedForNewDragonStakeInstance();
        }

        // Deploy a new DragonStake instance
        _deployDragonStakeInstance();
    }

    /**
     * @notice Mints the initial liquidity for the DragonX token.
     * @dev This function mints a specified amount of tokens and sets up the minting phases.
     * It can only be called once by the authorized address.
     * @param amount The amount of DragonX tokens to be minted for initial liquidity.
     */
    function mintInitialLiquidity(uint256 amount) external {
        // Cache state variables
        address dragonBuyAndBurnAddress_ = dragonBuyAndBurnAddress;

        // Verify that the caller is authorized to mint initial liquidity
        require(msg.sender == dragonBuyAndBurnAddress_, "not authorized");

        // Ensure that initial liquidity hasn't been minted before
        require(
            initalLiquidityMinted == InitialLiquidityMinted.No,
            "already minted"
        );

        // Mint the specified amount of DragonX tokens to the authorized address
        _mint(dragonBuyAndBurnAddress_, amount);

        // Update the state to reflect that initial liquidity has been minted
        initalLiquidityMinted = InitialLiquidityMinted.Yes;

        // Set up the minting phase timings
        uint256 currentTimestamp = block.timestamp;
        uint256 secondsUntilMidnight = 86400 - (currentTimestamp % 86400);

        // The mint phase is open for 84 days (12 weeks) and begins at midnight
        // once contracts are fully set up
        uint256 mintPhaseBegin_ = currentTimestamp + secondsUntilMidnight;

        // Update storage
        mintPhaseBegin = mintPhaseBegin_;

        // Set mint phase end
        mintPhaseEnd = mintPhaseBegin_ + 84 days;

        // Allow the first stake after 7 days of mint-phase begin
        nextStakeTs = mintPhaseBegin_ + 7 days;
    }

    /**
     *  Token Burn Function
     * @notice Allows a token holder to burn all of their tokens.
     * @dev Burns the entire token balance of the caller. This function calls `_burn`
     *      with the caller's address and their full token balance.
     *      This function can be called by any token holder wishing to burn their tokens.
     *      Tokens burned are permanently removed from the circulation.
     * @custom:warning WARNING: This function will irreversibly burn all tokens in the caller's account.
     * Ensure you understand the consequences before calling.
     */
    function burn() external {
        address sender = _msgSender();
        _burn(sender, balanceOf(sender));
    }

    /**
     * @notice Calculates the total number of stakes opened across all DragonStake contract instances.
     * @dev This function iterates over all the DragonStake contract instances recorded in the contract:
     *      1. For each DragonStake contract, it gets a reference to the contract instance.
     *      2. It then calls the `openedStakes` function on each instance to get the number of opened stakes.
     *      3. These values are summed up to calculate the total number of stakes opened across all instances.
     * @return totalStakes The total number of stakes opened across all DragonStake contract instances.
     */
    function totalStakesOpened() external view returns (uint256 totalStakes) {
        // Iterate over all DragonStake contract instances
        for (uint256 idx; idx < numDragonStakeContracts; idx++) {
            // Get a reference to each DragonStake contract
            DragonStake dragonStake = DragonStake(
                payable(dragonStakeContracts[idx])
            );

            // Add the stakes opened by this DragonStake instance
            totalStakes += dragonStake.openedStakes();
        }
    }

    /**
     * @dev Calculate the incentive fee a user will receive for calling the claim function.
     * This function computes the fee based on the total amount of Ethereum claimable
     * and a predefined incentive fee rate.
     *
     * @notice Used to determine the fee awarded for claiming Ethereum.
     *
     * @return fee The calculated incentive fee, represented as a uint256.
     * This value is calculated by taking the product of `totalEthClaimable()` and
     * `INCENTIVE_FEE`, then dividing by `BASIS` to normalize the fee calculation.
     */
    function incentiveFeeForClaim() external view returns (uint256 fee) {
        fee = (totalEthClaimable() * INCENTIVE_FEE) / BASIS;
    }

    /**
     * @dev Checks all DragonStake contract instances to determine if any stake has reached maturity.
     *      Iterates through each DragonStake contract instance and checks for stakes that have reached maturity.
     *      If a stake has reached maturity in a particular instance, it returns true along with the instance's address and the ID.
     *      If no stakes have reached maturity in any instance, it returns false and a zero address and zero for the ID.
     * @return hasStakesToEnd A boolean indicating if there is at least one stake that has reached maturity.
     * @return instanceAddress The address of the DragonStake contract instance that has a stake which reached maturity.
     * @return sId The ID of the stake which reached maturity
     *         Returns zero address if no such instance is found.
     * @notice This function is used to identify if and where stakes have reached maturity across multiple contract instances.
     */
    function stakeReachedMaturity()
        external
        view
        returns (bool hasStakesToEnd, address instanceAddress, uint256 sId)
    {
        // Iterate over all DragonStake contract instances
        for (uint256 idx; idx < numDragonStakeContracts; idx++) {
            address instance = dragonStakeContracts[idx];

            // Get a reference to each DragonStake contract
            DragonStake dragonStake = DragonStake(payable(instance));

            (bool reachedMaturity, uint256 id) = dragonStake
                .stakeReachedMaturity();

            // Exit if this instance contains a stake that reached maturity
            if (reachedMaturity) {
                return (true, instance, id);
            }
        }

        return (false, address(0), 0);
    }

    /**
     * @dev Sets the address used for buying and burning DRAGONX tokens.
     * @notice This function can only be called by the contract owner.
     * @param dragonBuyAndBurn The address to be set for the DRAGONX buy and burn operation.
     * If this address is the zero address, the transaction is reverted.
     * @custom:throws InvalidAddress if the provided address is the zero address.
     */
    function setDragonBuyAndBurnAddress(
        address dragonBuyAndBurn
    ) external onlyOwner {
        if (dragonBuyAndBurn == address(0)) {
            revert InvalidAddress();
        }
        dragonBuyAndBurnAddress = dragonBuyAndBurn;
    }

    /**
     * @dev Sets the address used for buying TITANX tokens.
     * @notice This function can only be called by the contract owner.
     * @param titanBuy The address to be set for the TITANX buy operation.
     * If this address is the zero address, the transaction is reverted.
     * @custom:throws InvalidAddress if the provided address is the zero address.
     */
    function setTitanBuyAddress(address titanBuy) external onlyOwner {
        if (titanBuy == address(0)) {
            revert InvalidAddress();
        }
        titanBuyAddress = titanBuy;
    }

    /**
     * @notice Transfers the accumulated balance of a specified asset from the Genesis Vault to the owner.
     * @dev This function allows the contract owner to claim assets accumulated in the Genesis Vault. It supports both Ether and ERC20 tokens.
     *      The function performs the following operations:
     *        1. Retrieves the balance of the specified asset from the `_genesisVault`.
     *        2. Sets the balance of the asset in the vault to zero, effectively resetting it.
     *        3. Checks that the retrieved balance is greater than zero, and reverts if it's not.
     *        4. If the asset is Ether (denoted by `asset` being the zero address), it transfers the Ether to the owner using `Address.sendValue`.
     *        5. If the asset is an ERC20 token, it transfers the token amount to the owner using `safeTransfer` from the ERC20 token's contract.
     * @param asset The address of the asset to be claimed. A zero address indicates Ether, and a non-zero address indicates an ERC20 token.
     */
    function claimGenesis(address asset) external onlyOwner {
        uint256 balance = _genesisVault[asset];
        _genesisVault[asset] = 0;

        require(balance > 0, "no balance");
        if (asset == address(0)) {
            Address.sendValue(payable(owner()), balance);
        } else {
            IERC20 erc20 = IERC20(asset);
            erc20.safeTransfer(owner(), balance);
        }
    }

    /**
     * @dev Updates the state when a TitanX stake has ended and the tokens are unstaked.
     *
     * This function should be called after unstaking TitanX tokens. It updates the vault
     * and the total amount of TitanX tokens that have been unstaked. This function can only
     * be called by an address that is allowed to end stakes (enforced by the `onlyDragonStake` modifier).
     *
     * @param amountUnstaked The amount of TitanX tokens that have been unstaked.
     * @notice This function is callable externally but restricted to allowed addresses (DragonStake contracts).
     * @notice It emits the `TitanStakesEnded` event after updating the total unstaked amount.
     */
    function stakeEnded(uint256 amountUnstaked) external onlyDragonStake {
        // Update vault (TitanX is transfered to DragonX)
        updateVault();

        // Update state
        totalTitanUnstaked += amountUnstaked;

        // Emit event
        emit TitanStakesEnded(_msgSender(), amountUnstaked);
    }

    // -----------------------------------------
    // Public functions
    // -----------------------------------------
    /**
     * @notice Updates the vault balance based on the current TITANX token balance.
     * @dev This function calculates the vault balance by subtracting the initial
     *      balance of TITANX tokens stored in `_genesisVault` from the current balance of
     *      TITANX tokens held by this contract.
     *      Steps involved in the function:
     *        1. Create an instance of the IERC20 interface for the TITANX token.
     *        2. Fetch the current TITANX token balance of this contract.
     *        3. Subtract the initial TITANX token balance (recorded in `_genesisVault`)
     *           from the current balance.
     *        4. Update the `vault` variable with the resulting value.
     *      The `vault` variable represents the net amount of TITANX tokens that have
     *      been accumulated in this contract since its inception (excluding the initial amount).
     *      This function should be called to reflect the latest state of the vault balance.
     */
    function updateVault() public {
        IERC20 titanX = IERC20(TITANX_ADDRESS);
        uint256 balance = titanX.balanceOf(address(this));

        vault = balance - _genesisVault[address(titanX)];
    }

    /**
     * @notice Calculates the total amount of ETH claimable from all DragonStake contract instances.
     * @dev Iterates through all deployed DragonStake contract instances and sums up the ETH claimable from each.
     *      This function is read-only and can be called externally.
     * @return claimable The total amount of ETH claimable across all DragonStake contract instances.
     */
    function totalEthClaimable() public view returns (uint256 claimable) {
        // Iterate over all DragonStake contract instances
        for (uint256 idx; idx < numDragonStakeContracts; idx++) {
            // Get a reference to each DragonStake contract
            DragonStake dragonStake = DragonStake(
                payable(dragonStakeContracts[idx])
            );

            // Add the claimable ETH from each DragonStake to the total
            claimable += dragonStake.totalEthClaimable();
        }
    }

    // -----------------------------------------
    // Internal functions
    // -----------------------------------------

    // -----------------------------------------
    // Private functions
    // -----------------------------------------
    /**
     * @dev Private function to deploy a DragonStake contract instance.
     *      It deploys a new DragonStake contract using create2 for deterministic addresses,
     *      and updates the activeDragonStakeContract and dragonStakeContracts mapping.
     *      An event DragonStakeInstanceCreated is emitted after successful deployment.
     *      This function is called by the deployNewDragonStakeInstance function.
     */
    function _deployDragonStakeInstance() private {
        // Deploy an instance of dragon staking contract
        bytes memory bytecode = type(DragonStake).creationCode;
        uint256 stakeContractId = numDragonStakeContracts;

        // Create a unique salt for deployment
        bytes32 salt = keccak256(
            abi.encodePacked(address(this), stakeContractId)
        );

        // Deploy a new DragonStake contract instance
        address newDragonStakeContract = Create2.deploy(0, salt, bytecode);

        // Set new contract as active
        activeDragonStakeContract = newDragonStakeContract;

        // Update storage
        dragonStakeContracts[stakeContractId] = newDragonStakeContract;

        // Allow the DragonStake instance to send ETH to DragonX
        _receiveEthAllowlist[newDragonStakeContract] = true;

        // For functions limited to DragonStake
        _dragonStakeAllowlist[newDragonStakeContract] = true;

        // Emit an event to track the creation of a new stake contract
        emit DragonStakeInstanceCreated(
            stakeContractId,
            newDragonStakeContract
        );

        // Increment the counter for DragonStake contracts
        numDragonStakeContracts += 1;
    }

    /**
     * @dev Private function to start a new stake using the currently active DragonStake instance.
     *      It transfers all TitanX tokens held by this contract to the active DragonStake instance
     *      and then initiates a new stake with the total amount transferred.
     *      This function is meant to be called internally by other contract functions.
     */
    function _startStake() private {
        // Cache state variables
        address activeDragonStakeContract_ = activeDragonStakeContract;

        // Initialize TitanX contract reference
        ITitanX titanX = ITitanX(TITANX_ADDRESS);
        DragonStake dragonStake = DragonStake(
            payable(activeDragonStakeContract_)
        );
        uint256 amountToStake = vault;
        vault = 0;

        // Transfer TitanX tokens to the active DragonStake contract
        titanX.safeTransfer(activeDragonStakeContract_, amountToStake);

        // Open a new stake with the total amount transferred
        dragonStake.stake();

        // Update states
        totalTitanStaked += amountToStake;

        // Emit event
        emit TitanStakeStarted(activeDragonStakeContract_, amountToStake);
    }
}
Contract Source Code
File 7 of 17: ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.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}.
 *
 * 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.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

    mapping(address account => mapping(address spender => 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 returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual 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 returns (uint8) {
        return 18;
    }

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

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual 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 `value`.
     */
    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

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

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `value` 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 value) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        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 `value`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `value`.
     */
    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

    /**
     * @dev Moves a `value` 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.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    /**
     * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
     * this function.
     *
     * Emits a {Transfer} event.
     */
    function _update(address from, address to, uint256 value) internal virtual {
        if (from == address(0)) {
            // Overflow check required: The rest of the code assumes that totalSupply never overflows
            _totalSupply += value;
        } else {
            uint256 fromBalance = _balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                // Overflow not possible: value <= fromBalance <= totalSupply.
                _balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                _totalSupply -= value;
            }
        } else {
            unchecked {
                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                _balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    /**
     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
     * Relies on the `_update` mechanism
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _mint(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(address(0), account, value);
    }

    /**
     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead
     */
    function _burn(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        _update(account, address(0), value);
    }

    /**
     * @dev Sets `value` 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.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    /**
     * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
     *
     * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
     * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
     * `Approval` event during `transferFrom` operations.
     *
     * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
     * true using the following override:
     * ```
     * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
     *     super._approve(owner, spender, value, true);
     * }
     * ```
     *
     * Requirements are the same as {_approve}.
     */
    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
        _allowances[owner][spender] = value;
        if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `value`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}
Contract Source Code
File 8 of 17: IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}
Contract Source Code
File 9 of 17: IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 */
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);
}
Contract Source Code
File 10 of 17: IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}
Contract Source Code
File 11 of 17: ITitanX.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.20;

// OpenZeppelin
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

// 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 for user stake
struct UserStake {
    uint256 sId;
    uint256 globalStakeId;
    UserStakeInfo stakeInfo;
}

// Interface for the contract
interface IStakeInfo {
    /**
     * @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 get stake info with stake id
     * @return stakeInfo stake info
     */
    function getUserStakeInfo(
        address user,
        uint256 id
    ) external view returns (UserStakeInfo memory);
}

/**
 * @title The TitanX interface used by DragonX to manages stakes
 * @author The DragonX devs
 */
interface ITitanX is IERC20, IStakeInfo {
    /**
     * @notice Start a new stake
     * @param amount The amount of TitanX tokens to stake
     * @param numOfDays The length of the stake in days
     */
    function startStake(uint256 amount, uint256 numOfDays) external;

    /**
     * @notice Claims available ETH payouts for a user based on their shares in various cycles.
     * @dev This function calculates the total reward from different cycles and transfers it to the caller.
     */
    function claimUserAvailableETHPayouts() external;

    /**
     * @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 Allows anyone to sync dailyUpdate manually.
     * @dev Function to be called for manually triggering the daily update process.
     * This function is public and can be called by any external entity.
     */
    function manualDailyUpdate() external;

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

    /**
     * @notice Create a new mint
     * @param mintPower The power of the mint, ranging from 1 to 100.
     * @param numOfDays The duration of the mint, ranging from 1 to 280 days.
     */
    function startMint(uint256 mintPower, uint256 numOfDays) external payable;

    /**
     * @notice Returns current mint cost
     * @return currentMintCost The current cost of minting.
     */
    function getCurrentMintCost() external view returns (uint256);

    /** @notice end a stake
     * @param id stake id
     */
    function endStake(uint256 id) external;
}
Contract Source Code
File 12 of 17: Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

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

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

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

    /**
     * @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 virtual onlyOwner {
        _transferOwnership(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 virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
Contract Source Code
File 13 of 17: Ownable2Step.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)

pragma solidity ^0.8.20;

import {Ownable} from "./Ownable.sol";

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is specified at deployment time in the constructor for `Ownable`. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private _pendingOwner;

    event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Returns the address of the pending owner.
     */
    function pendingOwner() public view virtual returns (address) {
        return _pendingOwner;
    }

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = _msgSender();
        if (pendingOwner() != sender) {
            revert OwnableUnauthorizedAccount(sender);
        }
        _transferOwnership(sender);
    }
}
Contract Source Code
File 14 of 17: ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

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

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    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
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // 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;
    }
}
Contract Source Code
File 15 of 17: SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}
Contract Source Code
File 16 of 17: Types.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.20;

/**
 * A simple enum to indicate of the initial liquidity for DragonX / TitanX pool has been minted
 */
enum InitialLiquidityMinted {
    No,
    Yes
}
Contract Source Code
File 17 of 17: draft-IERC6093.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}
Settings
{
  "compilationTarget": {
    "contracts/DragonX.sol": "DragonX"
  },
  "evmVersion": "paris",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "none"
  },
  "optimizer": {
    "enabled": true,
    "runs": 9999
  },
  "remappings": []
}
ABI
[{"inputs":[{"internalType":"address","name":"titanBuyAddress_","type":"address"},{"internalType":"address","name":"dragonBuyAndBurnAdddress_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"CooldownPeriodActive","type":"error"},{"inputs":[],"name":"Create2EmptyBytecode","type":"error"},{"inputs":[],"name":"Create2FailedDeployment","type":"error"},{"inputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"Create2InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC20InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC20InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC20InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"ERC20InvalidSpender","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InsufficientTitanXAllowance","type":"error"},{"inputs":[],"name":"InsufficientTitanXBalance","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidCaller","type":"error"},{"inputs":[],"name":"LiquidityNotMintedYet","type":"error"},{"inputs":[],"name":"MintingNotYetActive","type":"error"},{"inputs":[],"name":"MintingPeriodOver","type":"error"},{"inputs":[],"name":"NoAdditionalStakesAllowed","type":"error"},{"inputs":[],"name":"NoEthClaimable","type":"error"},{"inputs":[],"name":"NoNeedForNewDragonStakeInstance","type":"error"},{"inputs":[],"name":"NoTokensToStake","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"uint256","name":"totalClaimed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"titanBuy","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"dragonBuyAndBurn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"genesis","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"incentiveFee","type":"uint256"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"stakeContractId","type":"uint256"},{"indexed":true,"internalType":"address","name":"stakeContractAddress","type":"address"}],"name":"DragonStakeInstanceCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"dragonStakeAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TitanStakeStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"dragonStakeAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TitanStakesEnded","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"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"activeDragonStakeContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claim","outputs":[{"internalType":"uint256","name":"claimedAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"claimGenesis","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deployNewDragonStakeInstance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dragonBuyAndBurnAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"dragonStakeContracts","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"incentiveFeeForClaim","outputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initalLiquidityMinted","outputs":[{"internalType":"enum InitialLiquidityMinted","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mintInitialLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mintPhaseBegin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintPhaseEnd","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintRatioWeekEight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintRatioWeekEleven","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintRatioWeekFive","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintRatioWeekFour","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintRatioWeekNine","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintRatioWeekOne","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintRatioWeekSeven","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintRatioWeekSix","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintRatioWeekTen","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintRatioWeekThree","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintRatioWeekTwelve","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintRatioWeekTwo","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextStakeTs","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numDragonStakeContracts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"dragonBuyAndBurn","type":"address"}],"name":"setDragonBuyAndBurnAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"titanBuy","type":"address"}],"name":"setTitanBuyAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountUnstaked","type":"uint256"}],"name":"stakeEnded","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakeReachedMaturity","outputs":[{"internalType":"bool","name":"hasStakesToEnd","type":"bool"},{"internalType":"address","name":"instanceAddress","type":"address"},{"internalType":"uint256","name":"sId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"titanBuyAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalEthClaimable","outputs":[{"internalType":"uint256","name":"claimable","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalEthClaimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalStakesOpened","outputs":[{"internalType":"uint256","name":"totalStakes","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalTitanStaked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalTitanUnstaked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","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":"value","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":"updateVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]