// Contract: YashimituGold
// Target function: function buyWarriors(address ref) public payable
/**
 ▄         ▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄         ▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄       ▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄         ▄ 
▐░▌       ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░░░░░░░░░░░▌▐░░▌     ▐░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌       ▐░▌
▐░▌       ▐░▌▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀▀▀ ▐░▌       ▐░▌ ▀▀▀▀█░█▀▀▀▀ ▐░▌░▌   ▐░▐░▌ ▀▀▀▀█░█▀▀▀▀  ▀▀▀▀█░█▀▀▀▀ ▐░▌       ▐░▌
▐░▌       ▐░▌▐░▌       ▐░▌▐░▌          ▐░▌       ▐░▌     ▐░▌     ▐░▌▐░▌ ▐░▌▐░▌     ▐░▌          ▐░▌     ▐░▌       ▐░▌
▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄█░▌     ▐░▌     ▐░▌ ▐░▐░▌ ▐░▌     ▐░▌          ▐░▌     ▐░▌       ▐░▌
▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌     ▐░▌     ▐░▌  ▐░▌  ▐░▌     ▐░▌          ▐░▌     ▐░▌       ▐░▌
 ▀▀▀▀█░█▀▀▀▀ ▐░█▀▀▀▀▀▀▀█░▌ ▀▀▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀█░▌     ▐░▌     ▐░▌   ▀   ▐░▌     ▐░▌          ▐░▌     ▐░▌       ▐░▌
     ▐░▌     ▐░▌       ▐░▌          ▐░▌▐░▌       ▐░▌     ▐░▌     ▐░▌       ▐░▌     ▐░▌          ▐░▌     ▐░▌       ▐░▌
     ▐░▌     ▐░▌       ▐░▌ ▄▄▄▄▄▄▄▄▄█░▌▐░▌       ▐░▌ ▄▄▄▄█░█▄▄▄▄ ▐░▌       ▐░▌ ▄▄▄▄█░█▄▄▄▄      ▐░▌     ▐░█▄▄▄▄▄▄▄█░▌
     ▐░▌     ▐░▌       ▐░▌▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░░░░░░░░░░░▌     ▐░▌     ▐░░░░░░░░░░░▌
      ▀       ▀         ▀  ▀▀▀▀▀▀▀▀▀▀▀  ▀         ▀  ▀▀▀▀▀▀▀▀▀▀▀  ▀         ▀  ▀▀▀▀▀▀▀▀▀▀▀       ▀       ▀▀▀▀▀▀▀▀▀▀▀ 
                                                                                                                     
 ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄            ▄▄▄▄▄▄▄▄▄▄                                                                   
▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌          ▐░░░░░░░░░░▌                                                                  
▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀█░▌▐░▌          ▐░█▀▀▀▀▀▀▀█░▌                                                                 
▐░▌          ▐░▌       ▐░▌▐░▌          ▐░▌       ▐░▌                                                                 
▐░▌ ▄▄▄▄▄▄▄▄ ▐░▌       ▐░▌▐░▌          ▐░▌       ▐░▌                                                                 
▐░▌▐░░░░░░░░▌▐░▌       ▐░▌▐░▌          ▐░▌       ▐░▌                                                                 
▐░▌ ▀▀▀▀▀▀█░▌▐░▌       ▐░▌▐░▌          ▐░▌       ▐░▌                                                                 
▐░▌       ▐░▌▐░▌       ▐░▌▐░▌          ▐░▌       ▐░▌                                                                 
▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄█░▌                                                                 
▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░▌                                                                  
 ▀▀▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀▀                                                                   
                                                                                                                     
*/

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IERC20 {
    function totalSupply() external view returns (uint256);

    function decimals() external view returns (uint8);

    function symbol() external view returns (string memory);

    function name() external view returns (string memory);

    function balanceOf(address account) external view returns (uint256);

    function transfer(address recipient, uint256 amount)
        external
        returns (bool);

    function allowance(address _owner, address spender)
        external
        view
        returns (uint256);

    function approve(address spender, uint256 amount) external returns (bool);

    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );
}

interface IDexRouter {
    function factory() external pure returns (address);

    function WETH() external pure returns (address);

    function addLiquidityETH(
        address token,
        uint256 amountTokenDesired,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    )
        external
        payable
        returns (
            uint256 amountToken,
            uint256 amountETH,
            uint256 liquidity
        );

    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable;

    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external;
}

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());
    }

    function owner() public view virtual returns (address) {
        return _owner;
    }

    modifier onlyOwner() {
        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);
    }
}

contract YashimituGold is Ownable {
    using SafeMath for uint256;

    address payable public dev;
    address public token;
    IDexRouter public router;

    uint256 public SPOILS_TO_HIRE_WARRIOR = 1728000;
    uint256 public REFERRAL = 30;
    uint256 public PERCENTS_DIVIDER = 1000;
    uint256 public BUYBACK_TAX = 10;
    uint256 public DEV_TAX_BUY = 40;
    uint256 public DEV_TAX_SELL = 90;
    uint256 public MARKET_SPOILS_DIVISOR = 2;

    uint256 public MIN_DEPOSIT_LIMIT = 0.005 ether;
    uint256 public MAX_WITHDRAW_LIMIT = 2 ether;
    uint256[5] public ROI_MAP = [
        20 ether,
        40 ether,
        60 ether,
        80 ether,
        100 ether
    ];

    uint256 public COMPOUND_BONUS = 5;
    uint256 public COMPOUND_MAX_TIMES = 10;
    uint256 public COMPOUND_DURATION = 12 * 60 * 60;
    uint256 public PROOF_OF_LIFE = 48 * 60 * 60;
    uint256 public WITHDRAWAL_TAX = 700;
    uint256 public COMPOUND_FOR_NO_TAX_WITHDRAWAL = 10;

    uint256 public totalStaked;
    uint256 public totalSuttles;
    uint256 public totalDeposits;
    uint256 public totalCompound;
    uint256 public totalRefBonus;
    uint256 public totalWithdrawn;
    uint256 public whitelisTime = 1672596000;
    uint256 public publicTime = 1672599600;

    uint256 public marketSpoils = 144000000000;
    uint256 PSN = 10000;
    uint256 PSNH = 5000;

    bool public whitelistStart;
    bool public publicStart;

    struct User {
        uint256 initialDeposit;
        uint256 userDeposit;
        uint256 warriors;
        uint256 claimedSpoils;
        uint256 lastHatch;
        address referrer;
        uint256 referralsCount;
        uint256 referralRewards;
        uint256 totalWithdrawn;
        uint256 dailyCompoundBonus;
        uint256 warriorsCompoundCount;
        uint256 lastWithdrawTime;
    }

    mapping(address => User) public users;

    constructor() {
        dev = payable(0x5d8BCfEAD5b1d02eeE6129477Df619eAf3939026);
        router = IDexRouter(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
        token = 0xFa548DC8b1ec4De65e36b155F352e33ef6257260;
    }

    function isContract(address addr) internal view returns (bool) {
        uint256 size;
        assembly {
            size := extcodesize(addr)
        }
        return size > 0;
    }

    function startPublicJourney() public onlyOwner {
        require(!publicStart, "Already started");
        publicStart = true;
    }

    function startWhitelistJourney() public onlyOwner {
        require(!whitelistStart, "Already started");
        whitelistStart = true;
    }

    function setTimesOfLaunch(uint256 _pub, uint256 _wl) public onlyOwner {
        publicTime = _pub;
        whitelisTime = _wl;
    }

    function buyMoreWarriors() public {
        if (whitelistStart) {
            require(
                IERC20(token).balanceOf(msg.sender) > 0,
                "Not whitelsited."
            );
        } else {
            require(publicStart, "Contract not yet Started.");
        }
        User storage user = users[msg.sender];
        require(
            block.timestamp.sub(user.lastHatch) >= COMPOUND_DURATION,
            "Wait for next compound"
        );
        compound(true);
    }

    function compound(bool isCompound) internal {
        User storage user = users[msg.sender];

        uint256 spoilsUsed = getMySpoils(msg.sender);
        uint256 spoilsForCompound = spoilsUsed;

        if (isCompound) {
            uint256 dailyCompoundBonus = getDailyCompoundBonus(
                msg.sender,
                spoilsForCompound
            );
            spoilsForCompound = spoilsForCompound.add(dailyCompoundBonus);
            uint256 spoilsUsedValue = calculateSpoilsSell(spoilsForCompound);
            user.userDeposit = user.userDeposit.add(spoilsUsedValue);
            totalCompound = totalCompound.add(spoilsUsedValue);
            if (user.dailyCompoundBonus <= COMPOUND_MAX_TIMES) {
                user.dailyCompoundBonus = user.dailyCompoundBonus.add(1);
            }
        }

        //add compoundCount for monitoring purposes.
        user.warriorsCompoundCount = user.warriorsCompoundCount.add(1);
        user.warriors = user.warriors.add(
            spoilsForCompound.div(SPOILS_TO_HIRE_WARRIOR)
        );
        totalSuttles = totalSuttles.add(
            spoilsForCompound.div(SPOILS_TO_HIRE_WARRIOR)
        );
        user.claimedSpoils = 0;
        user.lastHatch = block.timestamp;

        marketSpoils = marketSpoils.add(spoilsUsed.div(MARKET_SPOILS_DIVISOR));
    }

    function sellSpoils() public {
        if (whitelistStart) {
            require(
                IERC20(token).balanceOf(msg.sender) > 0,
                "Not whitelsited."
            );
        } else {
            require(publicStart, "Contract not yet Started.");
        }

        User storage user = users[msg.sender];
        uint256 hasSpoils = getMySpoils(msg.sender);
        uint256 spoilsValue = calculateSpoilsSell(hasSpoils);

        /** 
            if user compound < to mandatory compound days**/
        if (user.dailyCompoundBonus < COMPOUND_FOR_NO_TAX_WITHDRAWAL) {
            //daily compound bonus count will not reset and spoilsValue will be deducted with x% feedback tax.
            spoilsValue = spoilsValue.sub(
                spoilsValue.mul(WITHDRAWAL_TAX).div(PERCENTS_DIVIDER)
            );
        } else {
            //set daily compound bonus count to 0 and spoilsValue will remain without deductions
            user.dailyCompoundBonus = 0;
            user.warriorsCompoundCount = 0;
        }

        user.lastWithdrawTime = block.timestamp;
        user.claimedSpoils = 0;
        user.lastHatch = block.timestamp;
        marketSpoils = marketSpoils.add(hasSpoils.div(MARKET_SPOILS_DIVISOR));

        // Antiwhale limit
        if (spoilsValue > MAX_WITHDRAW_LIMIT) {
            buy(msg.sender, address(0), spoilsValue.sub(MAX_WITHDRAW_LIMIT));
            spoilsValue = MAX_WITHDRAW_LIMIT;
        }
        if (spoilsValue > getBalance()) {
            buy(msg.sender, address(0), spoilsValue.sub(getBalance()));
            spoilsValue = getBalance();
        }

        uint256 spoilsPayout = spoilsValue.sub(takeFees(spoilsValue, false));
        payable(msg.sender).transfer(spoilsPayout);
        user.totalWithdrawn = user.totalWithdrawn.add(spoilsPayout);
        totalWithdrawn = totalWithdrawn.add(spoilsPayout);
    }