账户
0xda...5eb1
0xDa...5eB1

0xDa...5eB1

$500
此合同的源代码已经过验证!
合同元数据
编译器
0.8.27+commit.40a35a09
语言
Solidity
合同源代码
文件 1 的 9: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();
        }
    }
}
合同源代码
文件 2 的 9:Codeup.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.27;

import {IWETH, IERC20} from "./interfaces/IWETH.sol";
import {IUniswapV2Router} from "./interfaces/IUniswapV2Router.sol";
import {IUniswapV2Factory} from "./interfaces/IUniswapV2Factory.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

///░█████╗░░█████╗░██████╗░███████╗██╗░░░██╗██████╗░░░░███████╗████████╗██╗░░██╗
///██╔══██╗██╔══██╗██╔══██╗██╔════╝██║░░░██║██╔══██╗░░░██╔════╝╚══██╔══╝██║░░██║
///██║░░╚═╝██║░░██║██║░░██║█████╗░░██║░░░██║██████╔╝░░░█████╗░░░░░██║░░░███████║
///██║░░██╗██║░░██║██║░░██║██╔══╝░░██║░░░██║██╔═══╝░░░░██╔══╝░░░░░██║░░░██╔══██║
///╚█████╔╝╚█████╔╝██████╔╝███████╗╚██████╔╝██║░░░░░██╗███████╗░░░██║░░░██║░░██║
///░╚════╝░░╚════╝░╚═════╝░╚══════╝░╚═════╝░╚═╝░░░░░╚═╝╚══════╝░░░╚═╝░░░╚═╝░░╚═╝

/// @title Codeup contract
/// @notice This contract is used for the Codeup game
contract Codeup is ReentrancyGuard {
    using SafeERC20 for IERC20;

    struct Tower {
        uint256 gameETH; /// @notice User's gameETH balance
        uint256 gameETHForWithdraw; /// @notice User's available for withdraw balance
        uint256 gameETHCollected; /// @notice User's earned gameETH balance
        uint256 yields; /// @notice User's yields
        uint256 timestamp; /// @notice User's registration timestamp
        uint256 min; /// @notice User's time in the tower
        uint256 totalGameETHSpent; /// @notice User's total gameETH spent
        uint256 totalGameETHReceived; /// @notice User's total gameETH received
        uint8[8] builders; /// @notice User's builders count on each floor
    }
    /// @notice Max amount of gameETH available for buying
    uint256 public constant MAX_GAMEETH_FOR_BUYING = 78650;
    /// @notice Precision for math operations
    uint256 private constant PRECISION = 100;
    /// @notice CodeupERC20 token amount for winner
    uint256 private constant TOKEN_AMOUNT_FOR_WINNER = 1 ether;
    /// @notice Token amount in ETH needed for first liquidity
    uint256 private constant MAX_FIRST_LIQUIDITY_AMOUNT = 0.001 ether;
    /// @notice Amount of game token for first liquidity
    uint256 private constant FIRST_LIQUIDITY_GAME_TOKEN = 10 ether;
    /// @notice Withdraw commission 33% for rewards pool, 33% for liquidity pool
    uint256 private constant WITHDRAW_COMMISSION = 66;
    /// @notice Deposit commission 10% for liquidity pool
    uint256 private constant DEPOSIT_COMMISSION = 10;
    /// @notice Min amount for adding liquidity
    uint256 private constant MIN_AMOUNT_FOR_ADDING_LIQUIDITY = 0.0001 ether;
    /// @notice Minutes in hour
    uint256 private constant MINUTES_IN_HOUR = 60;
    /// @notice Max minutes for sync tower
    uint256 private constant MAX_MINUTES_FOR_SYNC = 24;

    /// @notice UniswapV2Router address
    address public immutable uniswapV2Router;
    /// @notice UniswapV2Factory address
    address public immutable uniswapV2Factory;
    /// @notice CodeupERC20 token address
    address public immutable codeupERC20;
    /// @notice WETH address
    address public immutable weth;
    /// @notice gameETH price
    uint256 public immutable gameETHPrice;
    /// @notice gameETH for withdraw rate
    uint256 public immutable gameETHForWithdrawRate;
    /// @notice Start date
    uint256 public immutable startUNIX;
    /// @notice Total builders count
    uint256 public totalBuilders;
    /// @notice Total towers count
    uint256 public totalTowers;
    /// @notice Total invested amount
    uint256 public totalInvested;
    /// @notice UniswapV2 pool address WETH/CodeupERC20
    address public uniswapV2Pool;
    /// @notice Last liquidity added timestamp
    uint256 public lastLiquidityAdded;

    /// @notice account claim status
    mapping(address => bool) public isClaimed;
    /// @notice User's tower info
    mapping(address => Tower) public towers;

    /// @notice Error messages
    error ZeroValue();
    error IncorrectBuilderId();
    error NotStarted();
    error TransferFailed();
    error MaxFloorsReached();
    error MaxGameETHReached();
    error NeedToBuyPreviousBuilder();
    error ClaimForbidden();
    error AlreadyClaimed();
    error OwnerIsNotAllowed();
    error LiquidityAddedRecently();
    error PoolNotCreated();

    /// @notice Emitted when user created tower
    /// @param user User's address
    event TowerCreated(address indexed user);
    /// @notice  Emitted when user added gameETH to the tower
    /// @param user User's address
    /// @param gameETHAmount gameETH amount
    /// @param ethAmount Spent ETH amount
    event AddGameETH(
        address indexed user,
        uint256 gameETHAmount,
        uint256 ethAmount,
        uint256 ethForPool
    );
    /// @notice Emitted when user withdraw gameETH
    /// @param user User's address
    /// @param amount gameETH amount
    event Withdraw(address indexed user, uint256 amount);
    /// @notice Emitted when user collect earned gameETH
    /// @param user User's address
    /// @param amount gameETH amount
    event Collect(address indexed user, uint256 amount);
    /// @notice Emitted when user upgrade tower
    /// @param user User's address
    /// @param floorId Floor id
    /// @param gameETH gameETH amount
    /// @param yields Yield amount
    event UpgradeTower(
        address indexed user,
        uint256 floorId,
        uint256 gameETH,
        uint256 yields
    );
    /// @notice Emitted when user sync tower
    /// @param user User's address
    /// @param yields Yield amount
    /// @param mins  number of minutes
    event SyncTower(address indexed user, uint256 yields, uint256 mins);
    /// @notice Emitted when uniswapV2 pool created
    /// @param pool Pool address
    event PoolCreated(address indexed pool);
    /// @notice Emitted when game token claimed
    /// @param account Account address
    /// @param amount Token amount
    event TokenClaimed(address indexed account, uint256 amount);
    /// @notice Emitted when liquidity locked
    event LiquidityLocked(uint256 indexed amount);
    /// @notice Emitted when liquidity added
    event LiquidityAdded(uint256 indexed amountA, uint256 indexed amountB);
    /// @notice Emitted when buy CodeupERC20
    event BuyCodeupERC20(uint256 indexed amount);

    /// @notice Contract constructor
    /// @param _startDate Start date
    /// @param _gameETHPrice gameETH price
    /// @param _uniswapV2Router Weighted pool factory address
    /// @param _codeupERC20 CodeupERC20 address
    constructor(
        uint256 _startDate,
        uint256 _gameETHPrice,
        address _uniswapV2Router,
        address _codeupERC20
    ) payable {
        _checkValue(_gameETHPrice);
        _checkValue(_startDate);
        _checkValue(_gameETHPrice / 1000);
        startUNIX = _startDate;
        gameETHPrice = _gameETHPrice;
        gameETHForWithdrawRate = _gameETHPrice / 1000;
        codeupERC20 = _codeupERC20;
        uniswapV2Router = _uniswapV2Router;
        weth = IUniswapV2Router(_uniswapV2Router).WETH();
        uniswapV2Factory = IUniswapV2Router(_uniswapV2Router).factory();
        lastLiquidityAdded = block.timestamp;
    }

    /// @notice Modifier for checking that game already started
    modifier onlyIfStarted() {
        require(block.timestamp > startUNIX, NotStarted());
        _;
    }

    /// @notice Add gameETH to the tower
    function addGameETH() external payable onlyIfStarted {
        uint256 tokenAmount = msg.value;
        uint256 gameETH = tokenAmount / gameETHPrice;
        _checkValue(gameETH);
        _checkMaxGameETH(msg.sender, gameETH);
        address user = msg.sender;
        uint256 totalInvestedBefore = totalInvested;
        totalInvested = totalInvestedBefore + tokenAmount;

        Tower storage tower = towers[user];
        if (tower.timestamp == 0) {
            totalTowers++;
            tower.timestamp = block.timestamp;
            emit TowerCreated(user);
        }
        tower.gameETH += gameETH;

        uint256 ethAmount = (tokenAmount * DEPOSIT_COMMISSION) / PRECISION;
        IWETH(weth).deposit{value: ethAmount}();
        emit AddGameETH(user, gameETH, tokenAmount, ethAmount);
    }

    /// @notice Withdraw earned gameETH from the tower
    function withdraw() external onlyIfStarted {
        address user = msg.sender;
        Tower storage tower = towers[user];
        uint256 contractBalance = _selfBalance();
        uint256 gameETH = tower.gameETHForWithdraw * gameETHForWithdrawRate;
        uint256 amount = contractBalance < gameETH ? contractBalance : gameETH;

        tower.gameETHForWithdraw -=
            (amount / gameETHForWithdrawRate) +
            (amount % gameETHForWithdrawRate == 0 ? 0 : 1);

        if (amount >= 1) {
            uint256 commission = (amount * WITHDRAW_COMMISSION) / PRECISION;
            amount -= commission;
            uint256 amountForPool = commission >> 1;
            IWETH(weth).deposit{value: amountForPool}();
        }

        (bool success, ) = user.call{value: amount}("");
        require(success, TransferFailed());
        emit Withdraw(user, amount);
    }

    /// @notice Collect earned gameETH from the tower to game balance
    function collect() external onlyIfStarted {
        address user = msg.sender;
        Tower storage tower = towers[user];
        _syncTower(user);
        tower.min = 0;
        uint256 gameETHCollected = tower.gameETHCollected;
        tower.gameETHForWithdraw += gameETHCollected;
        tower.gameETHCollected = 0;
        emit Collect(user, gameETHCollected);
    }

    /// @notice Reinvest earned gameETH to the tower
    function reinvest() external nonReentrant onlyIfStarted {
        address user = msg.sender;
        uint256 contractBalance = _selfBalance();
        Tower storage tower = towers[user];
        uint256 gameETHForWithdrawCached = tower.gameETHForWithdraw;
        _checkValue(gameETHForWithdrawCached);
        uint256 maxGameEthForBuy = getMaxGameEthForBuying(user);
        uint256 withdrawRate = gameETHForWithdrawRate;
        uint256 predictedGameETH = (gameETHForWithdrawCached * withdrawRate) /
            gameETHPrice;
        uint256 availableGameETHForReinvest = predictedGameETH <=
            maxGameEthForBuy
            ? gameETHForWithdrawCached
            : maxGameEthForBuy * (gameETHPrice / withdrawRate);
        uint256 gameETHForWithdraw = availableGameETHForReinvest * withdrawRate;
        uint256 amount = contractBalance < gameETHForWithdraw
            ? contractBalance
            : gameETHForWithdraw;

        tower.gameETHForWithdraw -=
            (amount / withdrawRate) +
            (amount % withdrawRate == 0 ? 0 : 1);

        emit Withdraw(user, amount);

        uint256 gameETH = amount / gameETHPrice;
        _checkValue(gameETH);
        uint256 totalInvestedBefore = totalInvested;
        totalInvested = totalInvestedBefore + amount;
        tower.gameETH += gameETH;

        uint256 ethAmount = (amount * DEPOSIT_COMMISSION) / PRECISION;
        IWETH(weth).deposit{value: ethAmount}();
        emit AddGameETH(user, gameETH, amount, ethAmount);
    }

    /// @notice Upgrade tower
    /// @param _floorId Floor id
    function upgradeTower(uint256 _floorId) external onlyIfStarted {
        require(_floorId < 8, MaxFloorsReached());
        address user = msg.sender;
        Tower storage tower = towers[user];
        if (_floorId != 0) {
            require(
                tower.builders[_floorId - 1] == 5,
                NeedToBuyPreviousBuilder()
            );
        }
        _syncTower(user);

        tower.builders[_floorId]++;
        totalBuilders++;
        uint256 buildersCount = tower.builders[_floorId];
        uint256 gameETHSpent = _getUpgradePrice(_floorId, buildersCount);
        tower.gameETH -= gameETHSpent;
        tower.totalGameETHSpent += gameETHSpent;
        uint256 yield = _getYield(_floorId, buildersCount);
        tower.yields += yield;
        emit UpgradeTower(msg.sender, _floorId, gameETHSpent, yield);
    }

    /// @notice Function perform claiming of game token
    /// Only users with 40 builders can claim game token
    /// Claiming possible only once.
    /// @param _account Account address
    /// @param _amountAMin Min amount of WETH for adding liquidity
    /// @param _amountBMin Min amount of CodeupERC20 for adding liquidity
    /// @param _amountOutMin Min amount of CodeupERC20 for buying
    function claimCodeupERC20(
        address _account,
        uint256 _amountAMin,
        uint256 _amountBMin,
        uint256 _amountOutMin
    ) external onlyIfStarted {
        require(isClaimAllowed(_account), ClaimForbidden());
        require(!isClaimed[_account], AlreadyClaimed());
        address currentContract = address(this);
        address wethMemory = weth;
        address codeupERC20Memory = codeupERC20;
        address routerMemory = uniswapV2Router;
        uint256 wethBalance = IERC20(wethMemory).balanceOf(currentContract);

        /// if pool not created, create pool
        if (uniswapV2Pool == address(0)) {
            uint256 maxFirstLiquidity = MAX_FIRST_LIQUIDITY_AMOUNT;
            uint256 firstLiquidity = wethBalance > maxFirstLiquidity
                ? maxFirstLiquidity
                : wethBalance;

            _addLiquidity(
                routerMemory,
                wethMemory,
                codeupERC20Memory,
                firstLiquidity,
                FIRST_LIQUIDITY_GAME_TOKEN,
                _amountAMin,
                _amountBMin,
                currentContract
            );

            uniswapV2Pool = IUniswapV2Factory(uniswapV2Factory).getPair(
                wethMemory,
                codeupERC20Memory
            );

            lastLiquidityAdded = block.timestamp;

            emit PoolCreated(uniswapV2Pool);
        } else {
            // buy codeupERC20 for WETH
            if (wethBalance >= MIN_AMOUNT_FOR_ADDING_LIQUIDITY) {
                uint256[] memory swapResult = _buyCodeupERC20(
                    routerMemory,
                    wethMemory,
                    codeupERC20Memory,
                    wethBalance >> 1,
                    _amountOutMin,
                    currentContract
                );

                _addLiquidity(
                    routerMemory,
                    wethMemory,
                    codeupERC20Memory,
                    swapResult[0],
                    swapResult[1],
                    _amountAMin,
                    _amountBMin,
                    currentContract
                );

                lastLiquidityAdded = block.timestamp;
            }
        }
        /// Set claim status to true, user can't claim token again.
        /// Claiming possible only once.
        isClaimed[_account] = true;
        /// Lock LP tokens
        _lockLP(currentContract);
        /// Transfer CodeupERC20 amount to the user
        uint256 tokenAmountForWinner = TOKEN_AMOUNT_FOR_WINNER;
        IERC20(codeupERC20Memory).safeTransfer(_account, tokenAmountForWinner);
        emit TokenClaimed(_account, tokenAmountForWinner);
    }

    /// @notice Function for force adding liquidity to pool. Can be called only once per week.
    /// @param _amountAMin Min amount of WETH for adding liquidity
    /// @param _amountBMin Min amount of CodeupERC20 for adding liquidity
    /// @param _amountOutMin Min amount of CodeupERC20 for buying
    function forceAddLiquidityToPool(
        uint256 _amountAMin,
        uint256 _amountBMin,
        uint256 _amountOutMin
    ) external {
        require(uniswapV2Pool != address(0), PoolNotCreated());
        address currentContract = address(this);
        address wethMemory = weth;
        address codeupERC20Memory = codeupERC20;
        address routerMemory = uniswapV2Router;
        uint256 wethBalance = IERC20(wethMemory).balanceOf(currentContract);
        _checkValue(wethBalance);
        require(
            block.timestamp - lastLiquidityAdded > 1 weeks,
            LiquidityAddedRecently()
        );

        uint256[] memory swapResult = _buyCodeupERC20(
            routerMemory,
            wethMemory,
            codeupERC20Memory,
            wethBalance >> 1,
            _amountOutMin,
            currentContract
        );

        _addLiquidity(
            routerMemory,
            wethMemory,
            codeupERC20Memory,
            swapResult[0],
            swapResult[1],
            _amountAMin,
            _amountBMin,
            currentContract
        );
        lastLiquidityAdded = block.timestamp;
    }

    /// @notice View function for checking if user can claim CodeupERC20
    /// @param _account Account address
    function isClaimAllowed(address _account) public view returns (bool) {
        uint8[8] memory builders = getBuilders(_account);
        uint8 count;
        for (uint8 i = 0; i < 8; i++) {
            count += builders[i];
        }

        if (count == 40) {
            return true;
        } else {
            return false;
        }
    }

    /// @notice Get user tower builders info
    /// @param _user User's address
    function getBuilders(address _user) public view returns (uint8[8] memory) {
        return towers[_user].builders;
    }

    /// @notice Calc user max available gameETH for buying
    /// @param _account User's address
    /// @return Max available gameETH for buying
    function getMaxGameEthForBuying(
        address _account
    ) public view returns (uint256) {
        Tower memory tower = towers[_account];
        uint256 totalGameETH = tower.gameETH + tower.totalGameETHSpent;
        return MAX_GAMEETH_FOR_BUYING - totalGameETH;
    }

    /// @notice Sync user tower info
    /// @param _user User's address
    function _syncTower(address _user) internal {
        Tower storage tower = towers[_user];
        _checkValue(tower.timestamp);
        if (tower.yields >= 1) {
            uint256 min = (block.timestamp - tower.timestamp) / MINUTES_IN_HOUR;
            if (min + tower.min > MAX_MINUTES_FOR_SYNC) {
                min = MAX_MINUTES_FOR_SYNC - tower.min;
            }
            uint256 yield = min * tower.yields;

            tower.gameETHCollected += yield;
            tower.totalGameETHReceived += yield;
            tower.min += min;
            emit SyncTower(_user, yield, min);
        }
        tower.timestamp = block.timestamp;
    }

    /// @notice Helper function for getting upgrade price for the floor and builder
    /// @param _floorId Floor id
    /// @param _builderId Builder id
    function _getUpgradePrice(
        uint256 _floorId,
        uint256 _builderId
    ) internal pure returns (uint256) {
        if (_builderId == 1)
            return [4340, 210, 420, 770, 1680, 2800, 5040, 6300][_floorId];
        if (_builderId == 2)
            return [70, 110, 210, 350, 630, 1120, 2800, 3500][_floorId];
        if (_builderId == 3)
            return [90, 140, 280, 490, 840, 1680, 3360, 5600][_floorId];
        if (_builderId == 4)
            return [110, 210, 350, 630, 1120, 2100, 3640, 6300][_floorId];
        if (_builderId == 5)
            return [150, 280, 490, 840, 1400, 2520, 4480, 11200][_floorId];
        revert IncorrectBuilderId();
    }

    /// @notice Helper function for getting yield for the floor and builder
    /// @param _floorId Floor id
    /// @param _builderId Builder id
    function _getYield(
        uint256 _floorId,
        uint256 _builderId
    ) internal pure returns (uint256) {
        if (_builderId == 1)
            return
                [4670, 2260, 2940, 6060, 11630, 16170, 22670, 17600][_floorId];
        if (_builderId == 2)
            return [410, 370, 1210, 2150, 3050, 4150, 8900, 3890][_floorId];
        if (_builderId == 3)
            return [1700, 510, 2180, 3170, 4320, 3510, 3570, 10300][_floorId];
        if (_builderId == 4)
            return [2180, 920, 2700, 4100, 5960, 8580, 9720, 10450][_floorId];
        if (_builderId == 5)
            return [2390, 980, 3810, 5510, 7420, 10070, 11880, 24160][_floorId];
        revert IncorrectBuilderId();
    }

    /// @notice Function performs buying CodeupERC20 from V2 pool
    function _buyCodeupERC20(
        address _router,
        address _weth,
        address _codeupERC20,
        uint256 _wethAmount,
        uint256 _amountOutMin,
        address _currentContract
    ) private returns (uint256[] memory swapResult) {
        address[] memory path = new address[](2);
        path[0] = _weth;
        path[1] = _codeupERC20;
        uint256[] memory amounts = IUniswapV2Router(_router).getAmountsOut(
            _wethAmount,
            path
        );
        IERC20(_weth).safeIncreaseAllowance(_router, _wethAmount);
        swapResult = IUniswapV2Router(_router).swapExactTokensForTokens(
            amounts[0],
            _amountOutMin,
            path,
            _currentContract,
            block.timestamp
        );
        emit BuyCodeupERC20(swapResult[1]);
    }

    /// @notice Function perfoms adding liquidity to uniswapV2Pool
    function _addLiquidity(
        address _router,
        address _tokenA,
        address _tokenB,
        uint256 _amountADesired,
        uint256 _amountBDesired,
        uint256 _amountAMin,
        uint256 _amountBMin,
        address _currentContract
    ) private {
        IERC20(_tokenA).safeIncreaseAllowance(_router, _amountADesired);
        IERC20(_tokenB).safeIncreaseAllowance(_router, _amountBDesired);
        IUniswapV2Router(_router).addLiquidity(
            _tokenA,
            _tokenB,
            _amountADesired,
            _amountBDesired,
            _amountAMin,
            _amountBMin,
            _currentContract,
            block.timestamp
        );
        emit LiquidityAdded(_amountADesired, _amountBDesired);
    }

    /// @notice Function for locking LP tokens.
    /// If contract has LP tokens, it will send them to address(0)
    function _lockLP(address _currentContract) private {
        address uniswapV2PoolCached = uniswapV2Pool;
        uint256 lpBalance = IERC20(uniswapV2PoolCached).balanceOf(
            _currentContract
        );
        if (lpBalance >= 1) {
            IERC20(uniswapV2PoolCached).safeTransfer(address(0), lpBalance);
        }
        emit LiquidityLocked(lpBalance);
    }

    /// @notice Function for getting contract balance
    function _selfBalance() private view returns (uint256 self) {
        assembly {
            self := selfbalance()
        }
    }

    /// @notice Function for checking max gameETH for buying
    function _checkMaxGameETH(address _account, uint256 _gameETH) private view {
        Tower memory tower = towers[_account];
        uint256 totalGameETH = tower.gameETH +
            _gameETH +
            tower.totalGameETHSpent;
        require(totalGameETH <= MAX_GAMEETH_FOR_BUYING, MaxGameETHReached());
    }

    /// @notice Function for checking value is not zero
    function _checkValue(uint256 _argument) private pure {
        require(_argument != 0, ZeroValue());
    }
}
合同源代码
文件 3 的 9: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);
}
合同源代码
文件 4 的 9: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);
}
合同源代码
文件 5 的 9:IUniswapV2Factory.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.27;

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

    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(uint256) external view returns (address pair);

    function allPairsLength() external view returns (uint256);

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

    function setFeeTo(address) external;

    function setFeeToSetter(address) external;
}
合同源代码
文件 6 的 9:IUniswapV2Router.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.27;

interface IUniswapV2Router {
    function factory() external pure returns (address);

    function WETH() external pure returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity);

    function getAmountsOut(
        uint256 amountIn,
        address[] calldata path
    ) external view returns (uint256[] memory amounts);

    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
}
合同源代码
文件 7 的 9:IWETH.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity 0.8.27;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/**
 * @dev Interface for WETH9.
 * See https://github.com/gnosis/canonical-weth/blob/0dd1ea3e295eef916d0c6223ec63141137d22d67/contracts/WETH9.sol
 */
interface IWETH is IERC20 {
    function deposit() external payable;

    function withdraw(uint256 amount) external;
}
合同源代码
文件 8 的 9: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;
    }
}
合同源代码
文件 9 的 9: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;
    }
}
设置
{
  "compilationTarget": {
    "contracts/Codeup.sol": "Codeup"
  },
  "evmVersion": "cancun",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": false,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"inputs":[{"internalType":"uint256","name":"_startDate","type":"uint256"},{"internalType":"uint256","name":"_gameETHPrice","type":"uint256"},{"internalType":"address","name":"_uniswapV2Router","type":"address"},{"internalType":"address","name":"_codeupERC20","type":"address"}],"stateMutability":"payable","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":"AlreadyClaimed","type":"error"},{"inputs":[],"name":"ClaimForbidden","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"IncorrectBuilderId","type":"error"},{"inputs":[],"name":"LiquidityAddedRecently","type":"error"},{"inputs":[],"name":"MaxFloorsReached","type":"error"},{"inputs":[],"name":"MaxGameETHReached","type":"error"},{"inputs":[],"name":"NeedToBuyPreviousBuilder","type":"error"},{"inputs":[],"name":"NotStarted","type":"error"},{"inputs":[],"name":"OwnerIsNotAllowed","type":"error"},{"inputs":[],"name":"PoolNotCreated","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"ZeroValue","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"gameETHAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethForPool","type":"uint256"}],"name":"AddGameETH","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"BuyCodeupERC20","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Collect","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"amountA","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"amountB","type":"uint256"}],"name":"LiquidityAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LiquidityLocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"}],"name":"PoolCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"yields","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mins","type":"uint256"}],"name":"SyncTower","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokenClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"TowerCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"floorId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gameETH","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"yields","type":"uint256"}],"name":"UpgradeTower","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"MAX_GAMEETH_FOR_BUYING","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"addGameETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_amountAMin","type":"uint256"},{"internalType":"uint256","name":"_amountBMin","type":"uint256"},{"internalType":"uint256","name":"_amountOutMin","type":"uint256"}],"name":"claimCodeupERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"codeupERC20","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collect","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountAMin","type":"uint256"},{"internalType":"uint256","name":"_amountBMin","type":"uint256"},{"internalType":"uint256","name":"_amountOutMin","type":"uint256"}],"name":"forceAddLiquidityToPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"gameETHForWithdrawRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gameETHPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getBuilders","outputs":[{"internalType":"uint8[8]","name":"","type":"uint8[8]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"getMaxGameEthForBuying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"isClaimAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isClaimed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastLiquidityAdded","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reinvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startUNIX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBuilders","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalInvested","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalTowers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"towers","outputs":[{"internalType":"uint256","name":"gameETH","type":"uint256"},{"internalType":"uint256","name":"gameETHForWithdraw","type":"uint256"},{"internalType":"uint256","name":"gameETHCollected","type":"uint256"},{"internalType":"uint256","name":"yields","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"min","type":"uint256"},{"internalType":"uint256","name":"totalGameETHSpent","type":"uint256"},{"internalType":"uint256","name":"totalGameETHReceived","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"uniswapV2Factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"uniswapV2Pool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"uniswapV2Router","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_floorId","type":"uint256"}],"name":"upgradeTower","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]