// Contract: auto_pool
// Target function: function sell(uint256 _amountOfTokens)
/**
 * www.the8020.ch
*/

pragma solidity ^0.6.0;

   /*==================================================================================
    =  The 80/20 is a Wealth Distribution system that is open for anyone to use.      =  
    =  We created this application with hopes that it will provide a steady stream    =
    =  of passive income for generations to come. The foundation that stands behind   =
    =  this product would like you to live happy, free, and prosperous.               =
    =  Stay tuned for more dApps from the GSG Global Marketing Group.                 =
    =  #LuckyRico #LACGold #JCunn24 #BoHarvey #LennyBones #WealthWithPhelps 	      =
    =  #ShahzainTariq >= developer of this smart contract		 				      =
    ================================================================================*/

interface IERC20 {
    
    function totalSupply() external view returns (uint256);

    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);
}


contract auto_pool is IERC20{
    
    using SafeMath for uint256;
    
    /*=================================
    =            MODIFIERS            =
    =================================*/
    // only people with tokens
    modifier onlybelievers () {
        require(myTokens() > 0);
        _;
    }
    
    // only people with profits
    modifier onlyhodler() {
        require(myDividends(true) > 0);
        _;
    }
    
 
    
    /*==============================
    =            EVENTS            =
    ==============================*/
    event onTokenPurchase(
        address indexed customerAddress,
        uint256 incomingEthereum,
        uint256 tokensMinted,
        address indexed referredBy,
        uint256 time,
        uint256 totalTokens
    );
    
    event onTokenSell(
        address indexed customerAddress,
        uint256 tokensBurned,
        uint256 ethereumEarned,
        uint256 time,
        uint256 totalTokens
    );
    
    event onReinvestment(
        address indexed customerAddress,
        uint256 ethereumReinvested,
        uint256 tokensMinted
    );
    
    event onWithdraw(
        address indexed customerAddress,
        uint256 ethereumWithdrawn
    );
    
    event distrubuteBonusFund(
        address,
        uint256
        );
        
    event amountDistributedToSponsor(
        address,
        address,
        uint256
        );
    
    // ERC20
    event Transfer(
        address indexed from,
        address indexed to,
        uint256 tokens,
        uint256 time
    );
    
    
    /*=====================================
    =            CONFIGURABLES            =
    =====================================*/
    string public name ;
    string public symbol ;
    uint8 public decimals ;
    uint8 internal dividendFee_ ;
    uint256 internal tokenPriceInitial_ ;
    uint256 internal tokenPriceIncremental_ ;
    uint256 internal magnitude;

    uint256 public tokenPool;
    uint256 public loyaltyPool;
    uint256 public developmentFund;
    uint256 public sponsorsPaid;
    uint256 public gsg_foundation;
    address dev1;
    address dev2;
    address GSGO_Official_LoyaltyPlan;
    uint256 public currentId;
    uint256 public day;
    uint256 public claimedLoyalty;
    uint256 public totalDeposited;    
    uint256 public totalWithdraw;
  
  
    
    
   /*================================
    =            DATASETS            =
    ================================*/
    // amount of shares for each address (scaled number)
    mapping(address => uint256) public tokenBalanceLedger_;
    mapping(address => uint256) public  referralBalance_;
    mapping (address => mapping (address => uint256)) private _allowances;
    mapping(address => int256) public payoutsTo_;
    mapping(address => basicData) public users;
    mapping(uint256 => address) public userList;
    uint256 internal tokenSupply_ = 0;
    uint256 internal profitPerShare_;
    uint256 internal profitperLoyalty;
    
    //Users's data set
    struct basicData{
        bool isExist;
        uint256 id;
        uint256 referrerId;
        address referrerAdd;
    }

    /*=======================================
    =            PUBLIC FUNCTIONS            =
    =======================================*/
    /*
    * -- APPLICATION ENTRY POINTS --  
    */
    constructor() public{
        name = "The-Eighty-Twenty";
        symbol = "GS20";
        decimals = 18;
        dividendFee_ = 10;
        tokenPriceInitial_ = 0.0000001 ether;
        tokenPriceIncremental_ = 0.00000001 ether;
        magnitude = 2**64;
        // "This is the distribution contract for holders of the GSG-Official (GSGO) Token."
        GSGO_Official_LoyaltyPlan = address(0x727395b95C90DEab2F220Ce42615d9dD0F44e187);
        dev1 = address(0x88F2E544359525833f606FB6c63826E143132E7b);
        dev2 = address(0x7cF196415CDD1eF08ca2358a8282D33Ba089B9f3);
        currentId = 0;
        day = now;
    
    }
    
     
    /**
     * Converts all incoming Ethereum to tokens for the caller, and passes down the referral address (if any)
     */
    function buy(address _referredAdd)
        public  
        payable
        returns(uint256)
    {
        require(msg.value >= 0.1 ether, "ERROR: minimun 0.1 ethereum ");
        require(_referredAdd != msg.sender,"ERROR: cannot become own ref");
        
        if(!users[msg.sender].isExist) register(msg.sender,_referredAdd);
        
        purchaseTokens(msg.value,_referredAdd);
         
        //Distributing Ethers
        loyaltyPool += ((msg.value.mul(12)).div(100));
        developmentFund += ((msg.value.mul(2)).div(100));
        gsg_foundation += ((msg.value.mul(2)).div(100));
        payable(GSGO_Official_LoyaltyPlan).transfer((msg.value.mul(2)).div(100));
        payable(dev1).transfer((msg.value.mul(1)).div(100));
        payable(dev2).transfer((msg.value.mul(1)).div(100));
        totalDeposited += msg.value;

    }
    
    receive() external payable {
         require(msg.value >= 0.1 ether, "ERROR: minimun 0.1 ethereum .");

     if(!users[msg.sender].isExist) register(msg.sender,address(0));

        purchaseTokens(msg.value,address(0));
         
        //Distributing Ethers
        loyaltyPool += ((msg.value.mul(12)).div(100));
        developmentFund += ( (msg.value.mul(2)).div(100));
        gsg_foundation += ((msg.value.mul(2)).div(100));
        payable(GSGO_Official_LoyaltyPlan).transfer((msg.value.mul(2)).div(100));
        payable(dev1).transfer((msg.value.mul(1)).div(100));
        payable(dev2).transfer((msg.value.mul(1)).div(100)); 
    }
    
    fallback()
        payable
        external
    {
    require(msg.value >= 0.1 ether, "ERROR: minimun 0.1 ethereum .");

     if(!users[msg.sender].isExist) register(msg.sender,address(0));

        purchaseTokens(msg.value,address(0));
         
        //Distributing Ethers
        loyaltyPool += ((msg.value.mul(12)).div(100));
        developmentFund += ( (msg.value.mul(2)).div(100));
        gsg_foundation += ((msg.value.mul(2)).div(100));
        payable(GSGO_Official_LoyaltyPlan).transfer((msg.value.mul(2)).div(100));
        payable(dev1).transfer((msg.value.mul(1)).div(100));
        payable(dev2).transfer((msg.value.mul(1)).div(100));    }
    
    /**
     * Converts all of caller's dividends to tokens.
     */
    function reinvest()
        onlyhodler()
        public
    {
        address _customerAddress = msg.sender;
        // fetch dividends
        uint256 _dividends = myDividends(false); // retrieve ref. bonus later in the code
        
        uint256 _loyaltyEth = loyaltyOf();
        if(_loyaltyEth > 0 ether){
            payable(address(_customerAddress)).transfer(_loyaltyEth);
                loyaltyPool -= _loyaltyEth;
                claimedLoyalty += _loyaltyEth;
                totalWithdraw += _loyaltyEth;
        }       
        
        // pay out the dividends virtually
        payoutsTo_[_customerAddress] +=  (int256) (_dividends * magnitude);

        
        // retrieve ref. bonus
        _dividends += referralBalance_[_customerAddress];
        referralBalance_[_customerAddress] = 0;

        // dispatch a buy order with the virtualized "withdrawn dividends"
        address refAdd = users[_customerAddress].referrerAdd;
        // dispatch a buy order with the virtualized "withdrawn dividends"
        uint256 _tokens = purchaseTokens(_dividends,refAdd);
        loyaltyPool += ((_dividends.mul(12)).div(100));
        developmentFund += ((_dividends.mul(2)).div(100));
        gsg_foundation += ((_dividends.mul(2)).div(100));
        payable(GSGO_Official_LoyaltyPlan).transfer((_dividends.mul(2)).div(100));
        payable(dev1).transfer((_dividends.mul(1)).div(100));
        payable(dev2).transfer((_dividends.mul(1)).div(100));
        // fire event
        emit onReinvestment(_customerAddress, _dividends, _tokens);
    }
    
    /**
     * Alias of sell() and withdraw().
     */
    function exit()
        public
    {
        // get token count for caller & sell them all
        address _customerAddress = msg.sender;
        uint256 _tokens = tokenBalanceLedger_[_customerAddress];
        if(_tokens > 0) sell(_tokens);
        
        
        withdraw();
    }

    /**
     * Withdraws all of the callers earnings.
     */
    function withdraw()
        onlyhodler()
        public
    {
        // setup data
        address _customerAddress = msg.sender;
        uint256 _dividends = myDividends(false); // get ref. bonus later in the code
        uint256 _loyaltyEth = loyaltyOf();

        // update dividend tracker
        payoutsTo_[_customerAddress] +=  (int256) (_dividends * magnitude);
        
        if(_loyaltyEth > 0 ether) {
            _dividends += _loyaltyEth;
            loyaltyPool -= _loyaltyEth;
            claimedLoyalty += _loyaltyEth;
        }       
        // add ref. bonus
        _dividends += referralBalance_[_customerAddress];
        referralBalance_[_customerAddress] = 0;
        
        totalWithdraw += _dividends;

        // delivery service
        payable(address(_customerAddress)).transfer(_dividends);
        
        // fire event
        emit onWithdraw(_customerAddress, _dividends);
    }