pragma solidity ^0.4.24;
 
 /*
 METAYEN ON ETH (YEN)
 */
 contract Metayen {
     /*=================================
     =            MODIFIERS            =
     =================================*/
     // only people with tokens
     modifier onlyBagholders() {
         require(myTokens() > 0);
         _;
     }
     
     // only people with profits
     modifier onlyStronghands() {
         require(myDividends(true) > 0);
         _;
     }
     
     // There are no admins set in constructor. This always reverts.
     modifier onlyAdministrator(){
         address _customerAddress = msg.sender;
         require(administrators[keccak256(_customerAddress)]);
         _;
     }
     
     /*==============================
     =            EVENTS            =
     ==============================*/
     event onTokenPurchase(
         address indexed customerAddress,
         uint256 incomingEthereum,
         uint256 tokensMinted,
         address indexed referredBy
     );
     
     event onTokenSell(
         address indexed customerAddress,
         uint256 tokensBurned,
         uint256 ethereumEarned
     );
     
     event onReinvestment(
         address indexed customerAddress,
         uint256 ethereumReinvested,
         uint256 tokensMinted
     );
     
     event onWithdraw(
         address indexed customerAddress,
         uint256 ethereumWithdrawn
     );
     
     // ERC20
     event Transfer(
         address indexed from,
         address indexed to,
         uint256 tokens
     );
     
     
     /*=====================================
     =            CONFIGURABLES            =
     =====================================*/
     string public name = "Metayen";
     string public symbol = "YEN";
     uint8 constant public decimals = 18;
     uint8 constant internal dividendFee_ = 10;
     uint256 constant internal tokenPriceInitial_ = 0.0000001 ether;
     uint256 constant internal tokenPriceIncremental_ = 0.00000001 ether;
     uint256 constant internal magnitude = 2**64;
     
     // masternodes for all.
     uint256 public stakingRequirement = 1;
     
     // ambassador program
     mapping(address => bool) internal ambassadors_;
     uint256 constant internal ambassadorMaxPurchase_ = 1 ether;
     uint256 constant internal ambassadorQuota_ = 20 ether;
     
     
     
    /*================================
     =            DATASETS            =
     ================================*/
     // amount of shares for each address (scaled number)
     mapping(address => uint256) internal tokenBalanceLedger_;
     mapping(address => uint256) internal referralBalance_;
     mapping(address => int256) internal payoutsTo_;
     mapping(address => uint256) internal ambassadorAccumulatedQuota_;
     uint256 internal tokenSupply_ = 0;
     uint256 internal profitPerShare_;
     
     // administrator list, always empty.
     mapping(bytes32 => bool) public administrators;
     
     // when this is set to true, only ambassadors can purchase tokens (this prevents a whale premine, it ensures a fairly distributed upper pyramid)
     bool public onlyAmbassadors = false;
     
 
 
     /*=======================================
     =            PUBLIC FUNCTIONS            =
     =======================================*/
     /*
     * -- APPLICATION ENTRY POINTS --  
     */
     function Metayen()
         public
     {
 
     }
     
      
     /**
      * Converts all incoming ethereum to tokens for the caller, and passes down the referral addy (if any)
      */
     function buy(address _referredBy)
         public
         payable
         returns(uint256)
     {
         purchaseTokens(msg.value, _referredBy);
     }
     
     /**
      * Fallback function to handle ethereum that was send straight to the contract
      * Unfortunately we cannot use a referral address this way.
      */
     function()
         payable
         public
     {
         purchaseTokens(msg.value, 0x0);
     }
     
     /**
      * Converts all of caller's dividends to tokens.
      */
     function reinvest()
         onlyStronghands()
         public
     {
         // fetch dividends
         uint256 _dividends = myDividends(false); // retrieve ref. bonus later in the code
         
         // pay out the dividends virtually
         address _customerAddress = msg.sender;
         payoutsTo_[_customerAddress] +=  (int256) (_dividends * magnitude);
         
         // retrieve ref. bonus
         _dividends += referralBalance_[_customerAddress];
         referralBalance_[_customerAddress] = 0;
         
         // dispatch a buy order with the virtualized "withdrawn dividends"
         uint256 _tokens = purchaseTokens(_dividends, 0x0);
         
         // fire event
         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);
         
         // lambo delivery service
         withdraw();
     }
 
     /**
      * Withdraws all of the callers earnings.
      */
     function withdraw()
         onlyStronghands()
         public
     {
         // setup data
         address _customerAddress = msg.sender;
         uint256 _dividends = myDividends(false); // get ref. bonus later in the code
         
         // update dividend tracker
         payoutsTo_[_customerAddress] +=  (int256) (_dividends * magnitude);
         
         // add ref. bonus
         _dividends += referralBalance_[_customerAddress];
         referralBalance_[_customerAddress] = 0;
         
         // lambo delivery service
         _customerAddress.transfer(_dividends);
         
         // fire event
         onWithdraw(_customerAddress, _dividends);
     }
     
     /**
      * Liquifies tokens to ethereum.
      */
     function sell(uint256 _amountOfTokens)
         onlyBagholders()
         public
     {
         // setup data
         address _customerAddress = msg.sender;
         // russian hackers BTFO
         require(_amountOfTokens <= tokenBalanceLedger_[_customerAddress]);
         uint256 _tokens = _amountOfTokens;
         uint256 _ethereum = tokensToEthereum_(_tokens);
         uint256 _dividends = SafeMath.div(_ethereum, dividendFee_);
         uint256 _taxedEthereum = SafeMath.sub(_ethereum, _dividends);
         
         // burn the sold tokens
         tokenSupply_ = SafeMath.sub(tokenSupply_, _tokens);
         tokenBalanceLedger_[_customerAddress] = SafeMath.sub(tokenBalanceLedger_[_customerAddress], _tokens);
         
         // update dividends tracker
         int256 _updatedPayouts = (int256) (profitPerShare_ * _tokens + (_taxedEthereum * magnitude));
         payoutsTo_[_customerAddress] -= _updatedPayouts;       
         
         // dividing by zero is a bad idea
         if (tokenSupply_ > 0) {
             // update the amount of dividends per token
             profitPerShare_ = SafeMath.add(profitPerShare_, (_dividends * magnitude) / tokenSupply_);
         }
         
         // fire event
         onTokenSell(_customerAddress, _tokens, _taxedEthereum);
     }
     
     
     /* Transfer tokens from the caller to a new holder. * No fee! */
    function transfer(address _toAddress, uint256 _amountOfTokens) onlyBagholders() public returns(bool) {
        // setup
        address _customerAddress = msg.sender;

        // make sure we have the requested tokens
        // also disables transfers until ambassador phase is over
        // ( we dont want whale premines )
        require(!onlyAmbassadors && _amountOfTokens <= tokenBalanceLedger_[_customerAddress]);

        // withdraw all outstanding dividends first
        if(myDividends(true) > 0) withdraw();

        // exchange tokens
        tokenBalanceLedger_[_customerAddress] = SafeMath.sub(tokenBalanceLedger_[_customerAddress], _amountOfTokens);
        tokenBalanceLedger_[_toAddress] = SafeMath.add(tokenBalanceLedger_[_toAddress], _amountOfTokens);

        // update dividend trackers
        payoutsTo_[_customerAddress] -= (int256) (profitPerShare_ * _amountOfTokens);
        payoutsTo_[_toAddress] += (int256) (profitPerShare_ * _amountOfTokens);

        // fire event
        Transfer(_customerAddress, _toAddress, _amountOfTokens);

        // ERC20
        return true;

        
     }
     
     // These do nothing and are only left in for ABI comaptibility reasons.
     /*----------  ADMINISTRATOR ONLY FUNCTIONS  ----------*/
     /**
      * In case the amassador quota is not met, the administrator can manually disable the ambassador phase.
      */
     function disableInitialStage()
         onlyAdministrator()
         public
     {
         return;
     }
     
     /**
      * Replace Admin.
      */
     function setAdministrator(bytes32 _identifier, bool _status)
         onlyAdministrator()
         public
     {
         return;
     }
     
     /**
      * Precautionary measures in case we need to adjust the masternode rate.
      */
     function setStakingRequirement(uint256 _amountOfTokens)
         onlyAdministrator()
         public
     {
         return;
     }
     
     /**
      * If we want to rebrand, we can.
      */
     function setName(string _name)
         onlyAdministrator()
         public
     {
         return;
     }
     
     /**
      * If we want to rebrand, we can.
      */
     function setSymbol(string _symbol)
         onlyAdministrator()
         public
     {
         return;
     }
 
     
     /*----------  HELPERS AND CALCULATORS  ----------*/
     /**
      * Method to view the current Ethereum stored in the contract
      * Example: totalEthereumBalance()
      */
     function totalEthereumBalance()
         public
         view
         returns(uint)
     {
         return this.balance;
     }
     
     /**
      * Retrieve the total token supply.
      */
     function totalSupply()
         public
         view
         returns(uint256)
     {
         return tokenSupply_;
     }
     
     /**
      * Retrieve the tokens owned by the caller.
      */
     function myTokens()
         public
         view
         returns(uint256)
     {
         address _customerAddress = msg.sender;
         return balanceOf(_customerAddress);
     }
     
     /**
      * Retrieve the dividends owned by the caller.
      * If `_includeReferralBonus` is to to 1/true, the referral bonus will be included in the calculations.
      * The reason for this, is that in the frontend, we will want to get the total divs (global + ref)
      * But in the internal calculations, we want them separate. 
      */ 
     function myDividends(bool _includeReferralBonus) 
         public 
         view 
         returns(uint256)
     {
         address _customerAddress = msg.sender;
         return _includeReferralBonus ? dividendsOf(_customerAddress) + referralBalance_[_customerAddress] : dividendsOf(_customerAddress) ;
     }
     
     /**
      * Retrieve the token balance of any single address.
      */
     function balanceOf(address _customerAddress)
         view
         public
         returns(uint256)
     {
         return tokenBalanceLedger_[_customerAddress];
     }
     
     /**
      * Retrieve the dividend balance of any single address.
      */
     function dividendsOf(address _customerAddress)
         view
         public
         returns(uint256)
     {
         return (uint256) ((int256)(profitPerShare_ * tokenBalanceLedger_[_customerAddress]) - payoutsTo_[_customerAddress]) / magnitude;
     }
     
     /**
      * Return the buy price of 1 individual token.
      */
     function sellPrice() 
         public 
         view 
         returns(uint256)
     {
         // our calculation relies on the token supply, so we need supply. Doh.
         if(tokenSupply_ == 0){
             return tokenPriceInitial_ - tokenPriceIncremental_;
         } else {
             uint256 _ethereum = tokensToEthereum_(1e18);
             uint256 _dividends = SafeMath.div(_ethereum, dividendFee_  );
             uint256 _taxedEthereum = SafeMath.sub(_ethereum, _dividends);
             return _taxedEthereum;
         }
     }
     
     /**
      * Return the sell price of 1 individual token.
      */
     function buyPrice() 
         public 
         view 
         returns(uint256)
     {
         // our calculation relies on the token supply, so we need supply. Doh.
         if(tokenSupply_ == 0){
             return tokenPriceInitial_ + tokenPriceIncremental_;
         } else {
             uint256 _ethereum = tokensToEthereum_(1e18);
             uint256 _dividends = SafeMath.div(_ethereum, dividendFee_  );
             uint256 _taxedEthereum = SafeMath.add(_ethereum, _dividends);
             return _taxedEthereum;
         }
     }
     
     /**
      * Function for the frontend to dynamically retrieve the price scaling of buy orders.
      */
     function calculateTokensReceived(uint256 _ethereumToSpend) 
         public 
         view 
         returns(uint256)
     {
         uint256 _dividends = SafeMath.div(_ethereumToSpend, dividendFee_);
         uint256 _taxedEthereum = SafeMath.sub(_ethereumToSpend, _dividends);
         uint256 _amountOfTokens = ethereumToTokens_(_taxedEthereum);
         
         return _amountOfTokens;
     }
     
     /**
      * Function for the frontend to dynamically retrieve the price scaling of sell orders.
      */
     function calculateEthereumReceived(uint256 _tokensToSell) 
         public 
         view 
         returns(uint256)
     {
         require(_tokensToSell <= tokenSupply_);
         uint256 _ethereum = tokensToEthereum_(_tokensToSell);
         uint256 _dividends = SafeMath.div(_ethereum, dividendFee_);
         uint256 _taxedEthereum = SafeMath.sub(_ethereum, _dividends);
         return _taxedEthereum;
     }
     
     
     /*==========================================
     =            INTERNAL FUNCTIONS            =
     ==========================================*/
     function purchaseTokens(uint256 _incomingEthereum, address _referredBy)
         internal
         returns(uint256)
     {
         // data setup
         address _customerAddress = msg.sender;
         uint256 _undividedDividends = SafeMath.div(_incomingEthereum, dividendFee_);
         uint256 _referralBonus = SafeMath.div(_undividedDividends, 3);
         uint256 _dividends = SafeMath.sub(_undividedDividends, _referralBonus);
         uint256 _taxedEthereum = SafeMath.sub(_incomingEthereum, _undividedDividends);
         uint256 _amountOfTokens = ethereumToTokens_(_taxedEthereum);
         uint256 _fee = _dividends * magnitude;
  
         // no point in continuing execution if OP is a poorfag russian hacker
         // prevents overflow in the case that the pyramid somehow magically starts being used by everyone in the world
         // (or hackers)
         // and yes we know that the safemath function automatically rules out the "greater then" equasion.
         require(_amountOfTokens > 0 && (SafeMath.add(_amountOfTokens,tokenSupply_) > tokenSupply_));
         
         // is the user referred by a masternode?
         if(
             // is this a referred purchase?
             _referredBy != 0x0000000000000000000000000000000000000000 &&
 
             // no cheating!
             _referredBy != _customerAddress &&
             
             // does the referrer have at least X whole tokens?
             // i.e is the referrer a godly chad masternode
             tokenBalanceLedger_[_referredBy] >= stakingRequirement
         ){
             // wealth redistribution
             referralBalance_[_referredBy] = SafeMath.add(referralBalance_[_referredBy], _referralBonus);
         } else {
             // no ref purchase
             // add the referral bonus back to the global dividends cake
             _dividends = SafeMath.add(_dividends, _referralBonus);
             _fee = _dividends * magnitude;
         }
         
         // we can't give people infinite ethereum
         if(tokenSupply_ > 0){
             
             // add tokens to the pool
             tokenSupply_ = SafeMath.add(tokenSupply_, _amountOfTokens);
  
             // take the amount of dividends gained through this transaction, and allocates them evenly to each shareholder
             profitPerShare_ += (_dividends * magnitude / (tokenSupply_));
             
             // calculate the amount of tokens the customer receives over his purchase 
             _fee = _fee - (_fee-(_amountOfTokens * (_dividends * magnitude / (tokenSupply_))));
         
         } else {
             // add tokens to the pool
             tokenSupply_ = _amountOfTokens;
         }
         
         // update circulating supply & the ledger address for the customer
         tokenBalanceLedger_[_customerAddress] = SafeMath.add(tokenBalanceLedger_[_customerAddress], _amountOfTokens);
         
         // Tells the contract that the buyer doesn't deserve dividends for the tokens before they owned them;
         //really i know you think you do but you don't
         int256 _updatedPayouts = (int256) ((profitPerShare_ * _amountOfTokens) - _fee);
         payoutsTo_[_customerAddress] += _updatedPayouts;
         
         // fire event
         onTokenPurchase(_customerAddress, _incomingEthereum, _amountOfTokens, _referredBy);
         
         return _amountOfTokens;
     }
 
     /**
      * Calculate Token price based on an amount of incoming ethereum
      * It's an algorithm, hopefully we gave you the whitepaper with it in scientific notation;
      * Some conversions occurred to prevent decimal errors or underflows / overflows in solidity code.
      */
     function ethereumToTokens_(uint256 _ethereum)
         internal
         view
         returns(uint256)
     {
         uint256 _tokenPriceInitial = tokenPriceInitial_ * 1e18;
         uint256 _tokensReceived = 
          (
             (
                 // underflow attempts BTFO
                 SafeMath.sub(
                     (sqrt
                         (
                             (_tokenPriceInitial**2)
                             +
                             (2*(tokenPriceIncremental_ * 1e18)*(_ethereum * 1e18))
                             +
                             (((tokenPriceIncremental_)**2)*(tokenSupply_**2))
                             +
                             (2*(tokenPriceIncremental_)*_tokenPriceInitial*tokenSupply_)
                         )
                     ), _tokenPriceInitial
                 )
             )/(tokenPriceIncremental_)
         )-(tokenSupply_)
         ;
   
         return _tokensReceived;
     }
     
     /**
      * Calculate token sell value.
      * It's an algorithm, hopefully we gave you the whitepaper with it in scientific notation;
      * Some conversions occurred to prevent decimal errors or underflows / overflows in solidity code.
      */
      function tokensToEthereum_(uint256 _tokens)
         internal
         view
         returns(uint256)
     {
 
         uint256 tokens_ = (_tokens + 1e18);
         uint256 _tokenSupply = (tokenSupply_ + 1e18);
         uint256 _etherReceived =
         (
             // underflow attempts BTFO
             SafeMath.sub(
                 (
                     (
                         (
                             tokenPriceInitial_ +(tokenPriceIncremental_ * (_tokenSupply/1e18))
                         )-tokenPriceIncremental_
                     )*(tokens_ - 1e18)
                 ),(tokenPriceIncremental_*((tokens_**2-tokens_)/1e18))/2
             )
         /1e18);
         return _etherReceived;
     }
     
     
     //This is where all your gas goes, sorry
     //Not sorry, you probably only paid 1 gwei
     function sqrt(uint x) internal pure returns (uint y) {
         uint z = (x + 1) / 2;
         y = x;
         while (z < y) {
             y = z;
             z = (x / z + z) / 2;
         }
     }
 }
 
 /**
  * @title SafeMath
  * @dev Math operations with safety checks that throw on error
  */
 library SafeMath {
 
     /**
     * @dev Multiplies two numbers, throws on overflow.
     */
     function mul(uint256 a, uint256 b) internal pure returns (uint256) {
         if (a == 0) {
             return 0;
         }
         uint256 c = a * b;
         assert(c / a == b);
         return c;
     }
 
     /**
     * @dev Integer division of two numbers, truncating the quotient.
     */
     function div(uint256 a, uint256 b) internal pure returns (uint256) {
         // assert(b > 0); // Solidity automatically throws when dividing by 0
         uint256 c = a / b;
         // assert(a == b * c + a % b); // There is no case in which this doesn't hold
         return c;
     }
 
     /**
     * @dev Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
     */
     function sub(uint256 a, uint256 b) internal pure returns (uint256) {
         assert(b <= a);
         return a - b;
     }
 
     /**
     * @dev Adds two numbers, throws on overflow.
     */
     function add(uint256 a, uint256 b) internal pure returns (uint256) {
         uint256 c = a + b;
         assert(c >= a);
         return c;
     }
 }