账户
0x9F...Ce87
0x9F...Ce87

0x9F...Ce87

$500
此合同的源代码已经过验证!
合同元数据
编译器
0.8.19+commit.7dd6d404
语言
Solidity
合同源代码
文件 1 的 1:Stellum.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.0;


abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

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

abstract contract Ownable is Context {
    address private _owner;

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

    
    constructor() {
        _transferOwnership(_msgSender());
    }

    
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    
    function owner() public view virtual returns (address) {
        return _owner;
    }

    
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

library Address {
    
    function isContract(address account) internal view returns (bool) {
        
        
        

        return account.code.length > 0;
    }

    
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                
                
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        
        if (returndata.length > 0) {
            
            
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

interface IERC721Receiver {
    
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

interface IUniswapV2Pair {
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);

    function name() external pure returns (string memory);
    function symbol() external pure returns (string memory);
    function decimals() external pure returns (uint8);
    function totalSupply() external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function allowance(address owner, address spender) external view returns (uint);

    function approve(address spender, uint value) external returns (bool);
    function transfer(address to, uint value) external returns (bool);
    function transferFrom(address from, address to, uint value) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);
    function PERMIT_TYPEHASH() external pure returns (bytes32);
    function nonces(address owner) external view returns (uint);

    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;

    event Mint(address indexed sender, uint amount0, uint amount1);
    event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
    event Swap(
        address indexed sender,
        uint amount0In,
        uint amount1In,
        uint amount0Out,
        uint amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint);
    function factory() external view returns (address);
    function token0() external view returns (address);
    function token1() external view returns (address);
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
    function price0CumulativeLast() external view returns (uint);
    function price1CumulativeLast() external view returns (uint);
    function kLast() external view returns (uint);

    function mint(address to) external returns (uint liquidity);
    function burn(address to) external returns (uint amount0, uint amount1);
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
    function skim(address to) external;
    function sync() external;

    function initialize(address, address) external;
}

interface IUniswapV2Factory {
    event PairCreated(address indexed token0, address indexed token1, address pair, uint);

    function feeTo() external view returns (address);
    function feeToSetter() external view returns (address);

    function getPair(address tokenA, address tokenB) external view returns (address pair);
    function allPairs(uint) external view returns (address pair);
    function allPairsLength() external view returns (uint);

    function createPair(address tokenA, address tokenB) external returns (address pair);

    function setFeeTo(address) external;
    function setFeeToSetter(address) external;
}

interface IUniswapV2Router01 {
    function factory() external pure returns (address);
    function WETH() external pure returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB, uint liquidity);
    function addLiquidityETH(
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETH(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountToken, uint amountETH);
    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETHWithPermit(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountToken, uint amountETH);
    function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapTokensForExactTokens(
        uint amountOut,
        uint amountInMax,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);
    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);

    function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
    function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
    function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}

interface IUniswapV2Router02 is IUniswapV2Router01 {
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountETH);
    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountETH);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable;
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
}

library Constants {

  
  uint256 internal constant PERCENT_PRECISION = 1e4;
  uint256 public constant ADMIN_FEE_PERCENT = 8_00; 

  
  uint8 public constant PLANET_LEVELS_NUMBER = 50;
  uint8 public constant NEXT_PLANET_THRESHOLD = 30;

  
  uint256 public constant BUY_ENERGY_MIN_VALUE = 0.001 ether;
  uint256 public constant TOKENS_WITHDRAW_LIMIT = 150_00; 

  uint256 public constant ENERGY_FOR_ETH = 2_000_000;
  uint256 public constant ENERGY_FOR_CRYSTAL = 110_00; 

  
  uint256 public constant NFT_PRICE = 0.015 ether;
  uint256 public constant NFT_MAX_SUPPLY = 10_000;

}

library GameModels {

  uint8 public constant REF_LEVELS_NUMBER = 7;

  struct Player {
    address referrer;
    address[] referrals;
    uint256[REF_LEVELS_NUMBER] referralsNumber;
    uint256 turnover;
    uint256[REF_LEVELS_NUMBER] turnoverLines;

    uint256 invested;
    uint256 referralRewardFromBuyEnergy;
    uint256 referralRewardFromExchange;
    uint256 withdrawn;
    uint256 withdrawnCrystals;
    uint256[2][REF_LEVELS_NUMBER] referralRewards;

    
    uint256 xp;
    uint8 level;
  }

  struct PlayerBalance {
    uint256 energy;
    uint256 crystals;

    uint256 lastCollectionTime;
    uint256 lastRocketPushTime;
  }

}

interface ICommonInterface {

  

  function mint(address to, uint256 amount) external;

  function increaseAllowance(address spender, uint256 addedValue) external returns (bool);

  function transferFrom(address from, address to, uint256 amount) external returns (bool);

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

  function burn(uint256 amount) external;

  

  function ownerOf(uint256 tokenId) external view returns (address);

  function safeTransferFrom(address from, address to, uint256 tokenId) external;

  

  function level(uint256 tokenId) external view returns (uint8);

  function markAsUsed(address playerAddr, uint256 tokenId) external;

  function upgrade(address playerAddr, uint256 tokenId, uint8 toLevel) external;

  function ETH_RECEIVER_ADDRESS() external returns (address);

}

library Events {

  event Registration(
    address indexed playerAddr,
    address indexed referrerAddr,
    uint256 registrationNumber,
    uint256 timestamp
  );

  event BuyEnergy(
    address indexed playerAddr,
    uint256 bnbAmount,
    uint256 timestamp
  );

  event ExchangeCrystals(
    address indexed playerAddr,
    uint256 crystals,
    uint256 timestamp
  );

  event ReferralReward(
    address indexed receiverAddr,
    address indexed payerAddr,
    uint256 rewardAmount, 
    uint256 bnbAmount,
    uint8 rewardType, 
    uint256 timestamp
  );

  event UpgradePlanet(
    address indexed playerAddr,
    uint8 indexed planetIdx,
    uint8 boughtLevels,
    uint8 resultLevel,
    uint256 timestamp
  );

  event AttachCharacter(
    address indexed playerAddr,
    uint256 tokenId,
    uint256 timestamp
  );

  event DetachCharacter(
    address indexed playerAddr,
    uint256 tokenId,
    uint256 timestamp
  );

  event BuyCharacter(
    address indexed playerAddr,
    address indexed coinAddr,
    uint256 tokensAmount,
    uint256 indexed chracterTokenId,
    uint256 timestamp
  );

  event UpgradeCharacter(
    address indexed playerAddr,
    uint256 indexed chracterTokenId,
    uint8 toLevel
  );

  event UpgradeCharacterDetails(
    address indexed playerAddr,
    address indexed coinAddr,
    uint256 ethAmount,
    uint256 tokensAmount,
    uint256 crystalsAmount,
    uint256 indexed chracterTokenId,
    uint8 toLevel,
    uint256 timestamp
  );

  event RatingUpdate(
    address indexed playerAddr,
    uint256 rating,
    uint256 timestamp
  );

  event CollectResources(
    address indexed playerAddr,
    uint256 energy,
    uint256 crystals,
    uint256 timestamp
  );

  event WithdrawCrystals(
    address indexed playerAddr,
    uint256 crystals,
    uint256 ethValue,
    uint256 timestamp
  );

  event PushRocket(
    address indexed playerAddr,
    uint256 timestamp
  );

  event CollectAchievementReward(
    address indexed playerAddr,
    uint8 indexed level,
    uint256 timestamp
  );

}

contract Stellum is Ownable, IERC721Receiver {

  using Address for address;

  uint8 public constant PLANETS_NUMBER = 8;
  uint256[PLANETS_NUMBER] public PLANET_LEVEL_PRICE = [
    1_000 ether,
    2_700 ether,
    7_500 ether,
    20_000 ether,
    55_000 ether,
    145_000 ether,
    400_000 ether,
    1_000_000 ether
  ];

  uint256[PLANETS_NUMBER] public PLANET_LEVEL_PROFITABILITY = [
    100_00, 
    102_00, 
    104_00, 
    106_00, 
    108_00, 
    111_00, 
    115_00, 
    120_00  
  ];

  uint8 public constant ACHIEVEMENTS_NUMBER = 12;
  uint256[ACHIEVEMENTS_NUMBER] public ACHIEVEMENTS_XP = [
    0,
    50_000,
    200_000,
    500_000,
    1_350_000,
    3_225_000,
    5_725_000,
    8_850_000,
    12_725_000,
    23_500_000,
    45_000_000,
    80_000_000
  ];

  uint256[ACHIEVEMENTS_NUMBER] public ACHIEVEMENTS_REWARDS = [
    0,
    1_500 ether,
    6_000 ether,
    15_000 ether,
    40_000 ether,
    96_000 ether,
    171_000 ether,
    265_000 ether,
    381_000 ether,
    700_000 ether,
    1_300_000 ether,
    2_300_000 ether
  ];

  uint8 public constant CHARACTER_LEVELS = 20;
  uint256[CHARACTER_LEVELS] public CHARACTER_LEVEL_UPGARE_PRICE_ETH = [
    0.015 ether, 0, 0, 0, 0,
    0, 0, 0, 0, 0,
    0.025 ether, 0, 0, 0, 0.05 ether,
    0, 0, 0, 0, 0.125 ether
  ];
  uint256[CHARACTER_LEVELS] public CHARACTER_LEVEL_UPGARE_PRICE_CRYSTALS = [
    0, 6_200 ether, 9_300 ether, 12_300 ether, 15_400 ether,
    18_500 ether, 21_600 ether, 24_700 ether, 27_800 ether, 30_900 ether,
    0, 39_900 ether, 49_700 ether, 60_500 ether, 0,
    64_000 ether, 78_200 ether, 93_700 ether, 114_000 ether, 0
  ];

  uint8 public constant REFERRAL_LEVELS_NUMBER = 5;
  uint8 public constant MAX_REFERRAL_LEVELS_NUMBER = 7;
  uint256[MAX_REFERRAL_LEVELS_NUMBER] public REFERRAL_PERCENTS = [5_00, 2_00, 1_00, 1_00, 1_00, 1_00, 1_00]; 

  uint256 private PRICE_BALANCER_PERCENT = 10500; 

  address immutable public DEFAULT_REFERRER;
  address public feeReceiver1;
  address public feeReceiver2;

  mapping(address => GameModels.Player) public players;
  mapping(address => GameModels.PlayerBalance) public balances;
  mapping(address => uint8[PLANETS_NUMBER]) planets;
  mapping(address => uint256) characters;

  uint256 public totalUsers;
  uint256 public totalSpent;
  uint256[PLANETS_NUMBER] public unlockedPlanets;
  uint256[PLANETS_NUMBER] public unlockedPlanetLevels;
  uint256 public totalCrystalsWithdrawn;

  ICommonInterface public token;
  ICommonInterface public character;

  IUniswapV2Router02 public uniswapRouter;
  IUniswapV2Pair public lpPair;

  
  struct Coin {
    address lpToken;
    bool reversed;
  }
  mapping(address => Coin) public coins;

  constructor(
    address defaultReferrerAddress,
    address nftTokenAddress,
    address erc20TokenAddress,
    address lpTokenContractAddress,
    address uniswapRouterAddress
  ) {
    require(defaultReferrerAddress != address(0x0), "Invalid default referrer address");
    require(lpTokenContractAddress.isContract(), "Invalid LP-token contract address");

    DEFAULT_REFERRER = defaultReferrerAddress;

    token = ICommonInterface(erc20TokenAddress);
    character = ICommonInterface(nftTokenAddress);

    lpPair = IUniswapV2Pair(lpTokenContractAddress);
    uniswapRouter = IUniswapV2Router02(uniswapRouterAddress);
  }

  receive() external payable {}

  function buyEnergy(address referrer) external payable {
    require(!msg.sender.isContract(), "User can't be a contract");
    require(msg.value >= Constants.BUY_ENERGY_MIN_VALUE, "Minimal amount is 0.001 ETH");

    GameModels.Player storage player = players[msg.sender];
    
    if (player.referrer == address(0x0)) {
      if (referrer == address(0x0) || referrer == msg.sender || players[referrer].referrer == address(0x0)) {
        referrer = DEFAULT_REFERRER;
      }
      player.referrer = referrer;
      players[referrer].referrals.push(msg.sender);

      totalUsers++;

      emit Events.RatingUpdate(referrer, getRating(referrer), block.timestamp);

      emit Events.Registration(
        msg.sender, referrer, totalUsers, block.timestamp
      );
    }

    player.invested+= msg.value;
    balances[msg.sender].energy+= msg.value * Constants.ENERGY_FOR_ETH;

    totalSpent+= msg.value;

    
    uint256 xp = msg.value * Constants.ENERGY_FOR_ETH * Constants.PERCENT_PRECISION / 1 ether;
    player.xp+= xp * getXPMultiplier(msg.sender);
    emit Events.RatingUpdate(msg.sender, getRating(msg.sender), block.timestamp);

    
    uint256 tokensAmount = getTokensAmount(msg.value);
    address ref = player.referrer;
    for (uint8 i = 0; i < MAX_REFERRAL_LEVELS_NUMBER; i++) {
      if (i < REFERRAL_LEVELS_NUMBER || getReferralLevelsNumber(ref) > i) {
        uint256 tokensRewardAmount = tokensAmount * REFERRAL_PERCENTS[i] / Constants.PERCENT_PRECISION;
        uint256 bnbRewardAmount = msg.value * REFERRAL_PERCENTS[i] / Constants.PERCENT_PRECISION;

        balances[ref].crystals+= tokensRewardAmount;
        players[ref].referralRewardFromBuyEnergy+= bnbRewardAmount;
        players[ref].referralRewards[i][0]+= bnbRewardAmount;

        emit Events.ReferralReward(
          ref,
          msg.sender,
          tokensRewardAmount,
          bnbRewardAmount,
          0,
          block.timestamp
        );
      }

      
      if (i == 0) {
        players[ref].xp+= xp * getXPMultiplier(ref) / 2;
        emit Events.RatingUpdate(ref, getRating(ref), block.timestamp);
      } else if (i == 1) {
        players[ref].xp+= xp * getXPMultiplier(ref) / 4;
        emit Events.RatingUpdate(ref, getRating(ref), block.timestamp);
      }

      
      players[ref].turnover+= msg.value;
      players[ref].turnoverLines[i]+= msg.value;

      players[ref].referralsNumber[i]++;

      ref = players[ref].referrer;
      if (ref == address(0x0)) {
        ref = DEFAULT_REFERRER;
      }
    }

    payable(owner()).transfer(msg.value * Constants.ADMIN_FEE_PERCENT / Constants.PERCENT_PRECISION);
    if (feeReceiver1 != address(0x0)) {
      payable(feeReceiver1).transfer(msg.value * 1_00 / Constants.PERCENT_PRECISION);
    }
    if (feeReceiver2 != address(0x0)) {
      payable(feeReceiver2).transfer(msg.value * 1_00 / Constants.PERCENT_PRECISION);
    }

    
    addLiquidity(address(this).balance);

    emit Events.BuyEnergy(
      msg.sender, msg.value, block.timestamp
    );
  }

  function upgradePlanet(uint8 planetIdx, uint8 levelsToBuy) public {
    require(!msg.sender.isContract(), "User can't be a contract");
    require(planetIdx >= 0 && planetIdx < PLANETS_NUMBER, "Invalid planet index");
    require(planetIdx == 0 || planets[msg.sender][planetIdx - 1] >= Constants.NEXT_PLANET_THRESHOLD, "This planed is closed. Upgrade previous planet first.");
    require(levelsToBuy <= Constants.PLANET_LEVELS_NUMBER, "Invalid levels to buy amount");

    if (planets[msg.sender][planetIdx] + levelsToBuy > Constants.PLANET_LEVELS_NUMBER) {
      levelsToBuy = Constants.PLANET_LEVELS_NUMBER - planets[msg.sender][planetIdx];
    }
    require(levelsToBuy > 0, "Invalid levels to buy amount");

    collectCrystalsAndEnergy();

    uint256 energyAmount = levelsToBuy * PLANET_LEVEL_PRICE[planetIdx];
    require(balances[msg.sender].energy >= energyAmount, "Not enough energy on the balance");

    if (planets[msg.sender][planetIdx] == 0) {
      unlockedPlanets[planetIdx]++;
    }
    unlockedPlanetLevels[planetIdx]+= levelsToBuy;

    balances[msg.sender].energy-= energyAmount;
    planets[msg.sender][planetIdx]+= levelsToBuy;

    emit Events.UpgradePlanet(
      msg.sender, planetIdx, levelsToBuy, planets[msg.sender][planetIdx], block.timestamp
    );
  }

  function quickUpgradePlanet(uint8 planetIdx, uint8 levelsToBuy) external {
    require(!msg.sender.isContract(), "User can't be a contract");
    require(planetIdx > 0 && planetIdx < PLANETS_NUMBER, "Invalid planet index");
    require(levelsToBuy <= Constants.PLANET_LEVELS_NUMBER, "Invalid levels to buy amount");

    if (planets[msg.sender][planetIdx - 1] < Constants.NEXT_PLANET_THRESHOLD) {
      upgradePlanet(planetIdx - 1, Constants.NEXT_PLANET_THRESHOLD - planets[msg.sender][planetIdx - 1]);
    }

    upgradePlanet(planetIdx, levelsToBuy);

    
  }

  function mayBeCollected(address playerAddr) public view returns (uint256 energy, uint256 crystals) {
    if (balances[playerAddr].lastCollectionTime == 0 || balances[playerAddr].lastCollectionTime == block.timestamp) {
      return (0 , 0);
    }

    GameModels.PlayerBalance memory balance = balances[playerAddr];
    uint256 startTime = balance.lastCollectionTime;
    uint256 endTime = block.timestamp;
    if (startTime < balance.lastRocketPushTime) {
      startTime = balance.lastRocketPushTime;
    }
    if (endTime > balance.lastRocketPushTime + getRocketFlightDuration(playerAddr)) {
      endTime = balance.lastRocketPushTime + getRocketFlightDuration(playerAddr);
    }

    if (startTime >= endTime) {
      return (0 , 0);
    }
    uint256 time = endTime - startTime;

    uint256 profit = 0;
    for (uint8 planetIdx = 0; planetIdx < PLANETS_NUMBER; planetIdx++) {
      if (planets[playerAddr][planetIdx] > 0) {
        profit+= PLANET_LEVEL_PRICE[planetIdx]
          * planets[playerAddr][planetIdx]
          * PLANET_LEVEL_PROFITABILITY[planetIdx]
          / Constants.PERCENT_PRECISION;
      } else {
        break;
      }
    }

    if (profit == 0) {
      return (0 , 0);
    }
    profit= profit * time / 30 days;
    crystals = profit * getPerformanceRatio(playerAddr) / Constants.PERCENT_PRECISION;

    return (profit - crystals, crystals);
  }

  function collectCrystalsAndEnergy() public {
    GameModels.PlayerBalance storage balance = balances[msg.sender];

    if (balance.lastCollectionTime == 0) {
      balance.lastCollectionTime = block.timestamp;
      balance.lastRocketPushTime = block.timestamp;

      return;
    }

    (uint256 energy, uint256 crystals) = mayBeCollected(msg.sender);
    if (energy == 0 || crystals == 0) {
      return;
    }

    balance.energy+= energy;
    balance.crystals+= crystals;
    balance.lastCollectionTime = block.timestamp;

    emit Events.CollectResources(
      msg.sender, energy, crystals, block.timestamp
    );
  }

  function instantBalance(address playerAddr) external view returns (uint256, uint256) {
    GameModels.PlayerBalance memory balance = balances[playerAddr];

    (uint256 energy, uint256 crystals) = mayBeCollected(playerAddr);

    return (balance.energy + energy, balance.crystals + crystals);
  }

  
  function changeCrystalsForEnergy(uint256 crystalsAmount) external {
    require(crystalsAmount > 0, "Invalid crystals amount");

    collectCrystalsAndEnergy();
    require(crystalsAmount <= balances[msg.sender].crystals, "Not enough crystals on the balance");

    balances[msg.sender].crystals-= crystalsAmount;
    balances[msg.sender].energy+= crystalsAmount * Constants.ENERGY_FOR_CRYSTAL / Constants.PERCENT_PRECISION;

    
    address ref = players[msg.sender].referrer;
    for (uint8 i = 0; i < MAX_REFERRAL_LEVELS_NUMBER; i++) {
      if (i < REFERRAL_LEVELS_NUMBER || getReferralLevelsNumber(ref) > i) {
        uint256 rewardAmount = crystalsAmount * REFERRAL_PERCENTS[i] / Constants.PERCENT_PRECISION;
        uint256 ethRewardAmount = rewardAmount / Constants.ENERGY_FOR_ETH;

        balances[ref].energy+= rewardAmount;
        players[ref].referralRewardFromExchange+= ethRewardAmount;
        players[ref].referralRewards[i][1]+= ethRewardAmount;

        emit Events.ReferralReward(
          ref,
          msg.sender,
          rewardAmount,
          ethRewardAmount,
          1,
          block.timestamp
        );
      }

      ref = players[ref].referrer;
      if (ref == address(0x0)) {
        ref = DEFAULT_REFERRER;
      }
    }

    emit Events.ExchangeCrystals(
      msg.sender, crystalsAmount, block.timestamp
    );
  }

  
  function withdrawCrystals(uint256 crystalsAmount) external {
    require(!msg.sender.isContract(), "User can't be a contract");
    require(crystalsAmount > 0, "Invalid crystals amount");

    collectCrystalsAndEnergy();
    require(crystalsAmount <= balances[msg.sender].crystals, "Not enough crystals on the balance");

    uint256 tokensMayBeWithdrawn = mayBeWithdrawn(msg.sender);
    require(tokensMayBeWithdrawn > 0, "You have reached withdrawal limit");
    if (crystalsAmount > tokensMayBeWithdrawn) {
      crystalsAmount = tokensMayBeWithdrawn;
    }

    GameModels.Player storage player = players[msg.sender];

    player.withdrawnCrystals+= crystalsAmount;
    totalCrystalsWithdrawn+= crystalsAmount;

    balances[msg.sender].crystals-= crystalsAmount;

    
    address[] memory path = new address[](2);
    path[0] = address(token);
    path[1] = uniswapRouter.WETH();

    token.mint(address(this), crystalsAmount);
    token.increaseAllowance(address(uniswapRouter), crystalsAmount);

    uint256[] memory amounts = uniswapRouter.swapExactTokensForETH(
      crystalsAmount,
      0,
      path,
      msg.sender,
      block.timestamp + 5 minutes
    );
    uint256 ethAmount = amounts[1];

    player.withdrawn+= ethAmount;

    if (PRICE_BALANCER_PERCENT > 0) {
      (uint256 ethReserved, ) = getTokenLiquidity();
      uint256 liquidity = lpPair.totalSupply()
        * ethAmount
        * PRICE_BALANCER_PERCENT
        / Constants.PERCENT_PRECISION
        / ethReserved;

      lpPair.approve(
        address(uniswapRouter),
        liquidity
      );

      (, uint256 amountETH) = uniswapRouter.removeLiquidityETH(
        address(token),
        liquidity, 
        0, 
        0, 
        address(this),
        block.timestamp + 5 minutes
      );

      path[0] = uniswapRouter.WETH();
      path[1] = address(token);
      amounts = uniswapRouter.swapExactETHForTokens {value: amountETH} (
        0,
        path,
        address(this),
        block.timestamp + 5 minutes
      );
    }

    emit Events.WithdrawCrystals(
      msg.sender, crystalsAmount, ethAmount, block.timestamp
    );
  }

  function mayBeWithdrawn(address playerAddr) public view returns (uint256) {
    GameModels.Player memory player = players[playerAddr];

    uint256 ethAmount =
      (player.invested + player.referralRewardFromExchange) * Constants.TOKENS_WITHDRAW_LIMIT / Constants.PERCENT_PRECISION
      + player.referralRewardFromBuyEnergy
      - player.withdrawn;

    return getTokensAmount(ethAmount);
  }

  function attachCharacter(uint256 tokenId) external {
    require(characters[msg.sender] == 0, "You have already attached other character");
    require(character.ownerOf(tokenId) == msg.sender, "You are not an owner of this NFT");

    collectCrystalsAndEnergy();

    character.safeTransferFrom(msg.sender, address(this), tokenId);
    characters[msg.sender] = tokenId;

    character.markAsUsed(msg.sender, tokenId);

    emit Events.AttachCharacter(
      msg.sender, tokenId, block.timestamp
    );

    
    balances[msg.sender].lastRocketPushTime = block.timestamp;
  }

  function detachCharacter() external {
    require(!msg.sender.isContract(), "User can't be a contract");
    require(characters[msg.sender] > 0, "You have no attached character");
    require(
      character.ownerOf(characters[msg.sender]) == address(this),
      "We have no this NFT on the contract"
    );

    collectCrystalsAndEnergy();

    character.safeTransferFrom(address(this), msg.sender, characters[msg.sender]);
    emit Events.DetachCharacter(
      msg.sender, characters[msg.sender], block.timestamp
    );
    characters[msg.sender] = 0;

    
    balances[msg.sender].lastRocketPushTime = block.timestamp;
  }

  function upgradeCharacter(uint8 toLevel, address coinAddress) external payable {
    require(!msg.sender.isContract(), "User can't be a contract");
    require(toLevel <= 20, "Invalid level value");
    require(characters[msg.sender] > 0, "You have no attached character");
    require(
      character.ownerOf(characters[msg.sender]) == address(this),
      "Character NFT isn't attached to the game"
    );
    require(
      coinAddress == address(0x0) || coins[coinAddress].lpToken != address(0x0),
      "Invalid coin address"
    );

    uint8 characterLvl = character.level(characters[msg.sender]);
    require(characterLvl < 20, "You have reached the maximum level");
    require(toLevel > characterLvl, "You can't downgrade character");

    collectCrystalsAndEnergy();

    uint256 upgradePriceETH = 0;
    uint256 upgradePriceCrystals = 0;
    for (uint8 lvl = characterLvl; lvl < toLevel; lvl++) {
      upgradePriceETH+= CHARACTER_LEVEL_UPGARE_PRICE_ETH[lvl];
      upgradePriceCrystals+= CHARACTER_LEVEL_UPGARE_PRICE_CRYSTALS[lvl];
    }

    uint256 coinsAmount;
    if (upgradePriceETH > 0) {
      if (coinAddress == address(0x0)) {
        require(msg.value == upgradePriceETH, "Invalid upgrade ETH amount");

        payable(character.ETH_RECEIVER_ADDRESS()).transfer(msg.value);
      } else {
        require(msg.value == 0, "Invalid upgrade ETH amount");
        coinsAmount = getTokensAmount(coinAddress, upgradePriceETH);

        ICommonInterface(coinAddress)
          .transferFrom(msg.sender, character.ETH_RECEIVER_ADDRESS(), coinsAmount);
      }
    }

    if (upgradePriceCrystals > 0) {
      require(
        balances[msg.sender].crystals >= upgradePriceCrystals,
        "Insufficient crystals balance"
      );

      balances[msg.sender].crystals-= upgradePriceCrystals;
    }
    character.upgrade(msg.sender, characters[msg.sender], toLevel);

    emit Events.RatingUpdate(msg.sender, getRating(msg.sender), block.timestamp);

    emit Events.UpgradeCharacter(msg.sender, characters[msg.sender], toLevel);

    emit Events.UpgradeCharacterDetails(
      msg.sender,
      coinAddress,
      upgradePriceETH,
      coinsAmount,
      upgradePriceCrystals,
      characters[msg.sender],
      toLevel,
      block.timestamp
    );

    
    balances[msg.sender].lastRocketPushTime = block.timestamp;
  }

  function pushRocket() external {
    require(!msg.sender.isContract(), "User can't be a contract");

    collectCrystalsAndEnergy();

    balances[msg.sender].lastRocketPushTime = block.timestamp;

    emit Events.PushRocket(msg.sender, block.timestamp);
  }

  
  function getRocketState(address playerAddr) external view returns (uint256 lastRocketPushTime, uint256 duration) {
    return (balances[playerAddr].lastRocketPushTime, getRocketFlightDuration(playerAddr));
  }

  function getETHAmount(uint256 tokensAmount) public view returns(uint256) {
    (uint256 reserve0, uint256 reserve1, ) = lpPair.getReserves();

    return tokensAmount * reserve0 / reserve1;
  }

  function getTokensAmount(uint256 amount) public view returns(uint256) {
    (uint256 reserve0, uint256 reserve1, ) = lpPair.getReserves();

    return amount * reserve1 / reserve0;
  }

  function getTokenLiquidity() public view returns (
    uint256 liquidityETH,
    uint256 liquiditySTM
  ) {
    (liquidityETH, liquiditySTM, ) = lpPair.getReserves();
  }

  function getTokensAmount(address tokenAddress, uint256 ethAmount) public view returns(uint256) {
    if (coins[tokenAddress].lpToken == address(0x0)) {
      return 0;
    }

    Coin memory coin = coins[tokenAddress];

    (uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(coin.lpToken).getReserves();
    uint256 ethAmount_ = reserve0;
    uint256 coinsAmount_ = reserve1;
    if (coin.reversed) {
      ethAmount_ = reserve1;
      coinsAmount_ = reserve0;
    }

    return ethAmount * coinsAmount_ / ethAmount_;
  }

  function addLiquidity(uint256 bnbAmount) private {
    uint256 amount = getTokensAmount(bnbAmount);

    token.mint(address(this), amount);
    token.increaseAllowance(address(uniswapRouter), amount);

    

    (uint256 amountToken, uint256 amountMATIC, uint256 liquidity) = uniswapRouter.addLiquidityETH {value: bnbAmount} (
      address(token),
      amount,
      0,
      0,
      address(this),
      block.timestamp + 5 minutes
    );

    

    
  }

  function setFeeReceiver1(address receiver) external onlyOwner {
    require(receiver != address(0x0), "Invalid fee receiver address");
    require(receiver != feeReceiver1, "Nothing to change");

    feeReceiver1 = receiver;

    
  }

  function setFeeReceiver2(address receiver) external onlyOwner {
    require(receiver != address(0x0), "Invalid fee receiver address");
    require(receiver != feeReceiver2, "Nothing to change");

    feeReceiver2 = receiver;

    
  }

  function changePriceBalancerPercent(uint256 percent) external onlyOwner {
    require(percent >= 0 && percent <= 20000, "Invalid percent amount (0 - 20000)");

    PRICE_BALANCER_PERCENT = percent;
  }

  function changePancakeRouterAddress(address newAddr) external onlyOwner {
    require(newAddr != address(0x0) && Address.isContract(newAddr), "Invalid PancakeRouter address");
    require(newAddr != address(uniswapRouter), "Address is already setted");

    uniswapRouter = IUniswapV2Router02(newAddr);
  }

  function burn(uint256 tokensAmount) external onlyOwner {
    if (tokensAmount == 0) {
      tokensAmount = token.balanceOf(address(this));
    }

    token.burn(tokensAmount);
  }

  function addCoin(
    address tokenAddress,
    address lpAddress,
    bool reversed
  ) external onlyOwner {
    require(coins[tokenAddress].lpToken == address(0x0), "Token already in list");
    require(tokenAddress.isContract(), "Invalid token address");
    require(lpAddress.isContract(), "Invalid LP token address");

    coins[tokenAddress] = Coin({
      lpToken: lpAddress,
      reversed: reversed
    });
  }

  function removeCoin(address tokenAddress) external onlyOwner {
    coins[tokenAddress] = Coin({
      lpToken: address(0x0),
      reversed: false
    });
  }

  function getReferralLevelsNumber(address playerAddr) public view returns (uint8 refLevelsNumber) {
    if (characters[playerAddr] == 0) {
      return REFERRAL_LEVELS_NUMBER;
    }

    uint8 characterLvl = character.level(characters[playerAddr]);
    if (characterLvl >= 15) {
      return (REFERRAL_LEVELS_NUMBER + 2);
    } else if (characterLvl >= 11) {
      return (REFERRAL_LEVELS_NUMBER + 1);
    }

    return REFERRAL_LEVELS_NUMBER;
  }

  
  function getPerformanceRatio(address playerAddr) public view returns (uint256 performanceRatio) {
    if (characters[playerAddr] == 0) {
      return 40_00;
    }

    uint8 characterLvl = character.level(characters[playerAddr]);

    return (40_00 + 1_00 * uint256(characterLvl));
  }

  function getRocketFlightDuration(address playerAddr) public view returns (uint256 rocketFlyDuration) {
    if (characters[playerAddr] == 0) {
      return (24 hours);
    }

    uint8 characterLvl = character.level(characters[playerAddr]);
    if (characterLvl >= 19) {
      return (24 hours + 240 hours);
    } else if (characterLvl >= 16) {
      return (24 hours + 144 hours);
    } else if (characterLvl >= 14) {
      return (24 hours + 120 hours);
    } else if (characterLvl >= 10) {
      return (24 hours + 96 hours);
    } else if (characterLvl >= 8) {
      return (24 hours + 72 hours);
    } else if (characterLvl >= 5) {
      return (24 hours + 48 hours);
    } else if (characterLvl >= 2) {
      return (24 hours + 24 hours);
    } else if (characterLvl == 1) {
      return (24 hours + 12 hours);
    }

    return (24 hours);
  }

  function getXPMultiplier(address playerAddr) public view returns (uint256 xpMultiplier) {
    if (characters[playerAddr] == 0) {
      return Constants.PERCENT_PRECISION;
    }

    uint8 characterLvl = character.level(characters[playerAddr]);
    if (characterLvl >= 13) {
      return Constants.PERCENT_PRECISION + 30_00;
    } else if (characterLvl >= 11) {
      return Constants.PERCENT_PRECISION + 25_00;
    } else if (characterLvl >= 9) {
      return Constants.PERCENT_PRECISION + 20_00;
    } else if (characterLvl >= 7) {
      return Constants.PERCENT_PRECISION + 15_00;
    } else if (characterLvl >= 5) {
      return Constants.PERCENT_PRECISION + 10_00;
    } else if (characterLvl >= 3) {
      return Constants.PERCENT_PRECISION + 5_00;
    }

    return Constants.PERCENT_PRECISION;
  }

  function getRatingMultiplier(address playerAddr) public view returns (uint256 ratingMultiplier) {
    if (characters[playerAddr] == 0) {
      return Constants.PERCENT_PRECISION;
    }

    uint8 characterLvl = character.level(characters[playerAddr]);

    return Constants.PERCENT_PRECISION + uint256(characterLvl) * 10_00;
  }

  function getRating(address playerAddr) public view returns (uint256 rating) {
    return (players[playerAddr].xp + players[playerAddr].referrals.length * 50_000 * Constants.PERCENT_PRECISION * Constants.PERCENT_PRECISION)
      * getRatingMultiplier(playerAddr) / Constants.PERCENT_PRECISION
      / Constants.PERCENT_PRECISION
      / Constants.PERCENT_PRECISION;
  }

  function collectAchievementReward() external {
    GameModels.Player storage player = players[msg.sender];

    uint8 lvl = player.level + 1;
    while (lvl < ACHIEVEMENTS_NUMBER) {
      if (player.xp >= ACHIEVEMENTS_XP[lvl] * Constants.PERCENT_PRECISION * Constants.PERCENT_PRECISION) {
        balances[msg.sender].energy+= ACHIEVEMENTS_REWARDS[lvl];
        lvl++;
      } else {
        break;
      }
    }

    player.level = lvl - 1;

    emit Events.CollectAchievementReward(msg.sender, player.level, block.timestamp);
  }

  function onERC721Received(
    address operator,
    address from,
    uint256 tokenId,
    bytes calldata data
  ) external pure returns (bytes4) {
    return this.onERC721Received.selector; 
  }

  function referrals(address playerAddr) external view returns (address[] memory) {
    return players[playerAddr].referrals;
  }

  function commonReferralStats(address playerAddr) external view returns (
    address referrer,
    uint256 referralsCount,
    uint256 structureVolume,
    uint256 turnover,
    address[] memory referralsList,
    uint256[MAX_REFERRAL_LEVELS_NUMBER] memory referralsNumber,
    uint256[MAX_REFERRAL_LEVELS_NUMBER] memory turnoverLines
  ) {
    GameModels.Player memory player = players[playerAddr];

    for (uint8 i = 0; i < MAX_REFERRAL_LEVELS_NUMBER; i++) {
      structureVolume+= player.referralsNumber[i];
    }

    return (
      player.referrer,
      player.referrals.length,
      structureVolume,
      player.turnover,
      player.referrals,
      player.referralsNumber,
      player.turnoverLines
    );
  }

  function getReferralRewards(address playerAddr) external view returns (
    uint256[] memory referralRewardsFromBuyEnergy, uint256[] memory referralRewardsFromExchange
  ) {
    GameModels.Player memory player = players[playerAddr];

    referralRewardsFromBuyEnergy = new uint256[](MAX_REFERRAL_LEVELS_NUMBER);
    referralRewardsFromExchange = new uint256[](MAX_REFERRAL_LEVELS_NUMBER);

    for (uint8 i = 0; i < MAX_REFERRAL_LEVELS_NUMBER; i++) {
      referralRewardsFromBuyEnergy[i] = player.referralRewards[i][0];
      referralRewardsFromExchange[i] = player.referralRewards[i][1];
    }
  }

  function getPlanetsStats() external view returns (
    uint256[] memory unlockedPlanetsStats,
    uint256[] memory unlockedPlanetLevelsStats
  ) {
    unlockedPlanetsStats = new uint256[](PLANETS_NUMBER);
    unlockedPlanetLevelsStats = new uint256[](PLANETS_NUMBER);

    for (uint8 i = 0; i < PLANETS_NUMBER; i++) {
      unlockedPlanetsStats[i] = unlockedPlanets[i];
      unlockedPlanetLevelsStats[i] = unlockedPlanetLevels[i];
    }
  }

  function getPlayerPlanets(address playerAddr) external view returns (uint8[PLANETS_NUMBER] memory) {
    return planets[playerAddr];
  }

  function buyEnergy() external payable {
    payable(msg.sender).transfer(msg.value);

    
  }

}
设置
{
  "compilationTarget": {
    "Stellum.sol": "Stellum"
  },
  "evmVersion": "paris",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"inputs":[{"internalType":"address","name":"defaultReferrerAddress","type":"address"},{"internalType":"address","name":"nftTokenAddress","type":"address"},{"internalType":"address","name":"erc20TokenAddress","type":"address"},{"internalType":"address","name":"lpTokenContractAddress","type":"address"},{"internalType":"address","name":"uniswapRouterAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"ACHIEVEMENTS_NUMBER","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"ACHIEVEMENTS_REWARDS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"ACHIEVEMENTS_XP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CHARACTER_LEVELS","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"CHARACTER_LEVEL_UPGARE_PRICE_CRYSTALS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"CHARACTER_LEVEL_UPGARE_PRICE_ETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_REFERRER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_REFERRAL_LEVELS_NUMBER","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PLANETS_NUMBER","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"PLANET_LEVEL_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"PLANET_LEVEL_PROFITABILITY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REFERRAL_LEVELS_NUMBER","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"REFERRAL_PERCENTS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"address","name":"lpAddress","type":"address"},{"internalType":"bool","name":"reversed","type":"bool"}],"name":"addCoin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"attachCharacter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balances","outputs":[{"internalType":"uint256","name":"energy","type":"uint256"},{"internalType":"uint256","name":"crystals","type":"uint256"},{"internalType":"uint256","name":"lastCollectionTime","type":"uint256"},{"internalType":"uint256","name":"lastRocketPushTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokensAmount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"referrer","type":"address"}],"name":"buyEnergy","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"buyEnergy","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"crystalsAmount","type":"uint256"}],"name":"changeCrystalsForEnergy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAddr","type":"address"}],"name":"changePancakeRouterAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"percent","type":"uint256"}],"name":"changePriceBalancerPercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"character","outputs":[{"internalType":"contract ICommonInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"coins","outputs":[{"internalType":"address","name":"lpToken","type":"address"},{"internalType":"bool","name":"reversed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collectAchievementReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collectCrystalsAndEnergy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"playerAddr","type":"address"}],"name":"commonReferralStats","outputs":[{"internalType":"address","name":"referrer","type":"address"},{"internalType":"uint256","name":"referralsCount","type":"uint256"},{"internalType":"uint256","name":"structureVolume","type":"uint256"},{"internalType":"uint256","name":"turnover","type":"uint256"},{"internalType":"address[]","name":"referralsList","type":"address[]"},{"internalType":"uint256[7]","name":"referralsNumber","type":"uint256[7]"},{"internalType":"uint256[7]","name":"turnoverLines","type":"uint256[7]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"detachCharacter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeReceiver1","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeReceiver2","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokensAmount","type":"uint256"}],"name":"getETHAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"playerAddr","type":"address"}],"name":"getPerformanceRatio","outputs":[{"internalType":"uint256","name":"performanceRatio","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPlanetsStats","outputs":[{"internalType":"uint256[]","name":"unlockedPlanetsStats","type":"uint256[]"},{"internalType":"uint256[]","name":"unlockedPlanetLevelsStats","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"playerAddr","type":"address"}],"name":"getPlayerPlanets","outputs":[{"internalType":"uint8[8]","name":"","type":"uint8[8]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"playerAddr","type":"address"}],"name":"getRating","outputs":[{"internalType":"uint256","name":"rating","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"playerAddr","type":"address"}],"name":"getRatingMultiplier","outputs":[{"internalType":"uint256","name":"ratingMultiplier","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"playerAddr","type":"address"}],"name":"getReferralLevelsNumber","outputs":[{"internalType":"uint8","name":"refLevelsNumber","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"playerAddr","type":"address"}],"name":"getReferralRewards","outputs":[{"internalType":"uint256[]","name":"referralRewardsFromBuyEnergy","type":"uint256[]"},{"internalType":"uint256[]","name":"referralRewardsFromExchange","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"playerAddr","type":"address"}],"name":"getRocketFlightDuration","outputs":[{"internalType":"uint256","name":"rocketFlyDuration","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"playerAddr","type":"address"}],"name":"getRocketState","outputs":[{"internalType":"uint256","name":"lastRocketPushTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTokenLiquidity","outputs":[{"internalType":"uint256","name":"liquidityETH","type":"uint256"},{"internalType":"uint256","name":"liquiditySTM","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"ethAmount","type":"uint256"}],"name":"getTokensAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getTokensAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"playerAddr","type":"address"}],"name":"getXPMultiplier","outputs":[{"internalType":"uint256","name":"xpMultiplier","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"playerAddr","type":"address"}],"name":"instantBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lpPair","outputs":[{"internalType":"contract IUniswapV2Pair","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"playerAddr","type":"address"}],"name":"mayBeCollected","outputs":[{"internalType":"uint256","name":"energy","type":"uint256"},{"internalType":"uint256","name":"crystals","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"playerAddr","type":"address"}],"name":"mayBeWithdrawn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"players","outputs":[{"internalType":"address","name":"referrer","type":"address"},{"internalType":"uint256","name":"turnover","type":"uint256"},{"internalType":"uint256","name":"invested","type":"uint256"},{"internalType":"uint256","name":"referralRewardFromBuyEnergy","type":"uint256"},{"internalType":"uint256","name":"referralRewardFromExchange","type":"uint256"},{"internalType":"uint256","name":"withdrawn","type":"uint256"},{"internalType":"uint256","name":"withdrawnCrystals","type":"uint256"},{"internalType":"uint256","name":"xp","type":"uint256"},{"internalType":"uint8","name":"level","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pushRocket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"planetIdx","type":"uint8"},{"internalType":"uint8","name":"levelsToBuy","type":"uint8"}],"name":"quickUpgradePlanet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"playerAddr","type":"address"}],"name":"referrals","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"removeCoin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"setFeeReceiver1","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"setFeeReceiver2","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract ICommonInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalCrystalsWithdrawn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSpent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalUsers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"uniswapRouter","outputs":[{"internalType":"contract IUniswapV2Router02","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"unlockedPlanetLevels","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"unlockedPlanets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"toLevel","type":"uint8"},{"internalType":"address","name":"coinAddress","type":"address"}],"name":"upgradeCharacter","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint8","name":"planetIdx","type":"uint8"},{"internalType":"uint8","name":"levelsToBuy","type":"uint8"}],"name":"upgradePlanet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"crystalsAmount","type":"uint256"}],"name":"withdrawCrystals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]