// Contract: DappIncubator
// Target function: function buy(address contractAddress, uint256 tokenAmount, address _referredBy) public nonReentrant returns(uint256)
// SPDX-License-Identifier: MIT

/**
 *  Program Name    : DappIncubator
 *  Website         : https://www.dappincubator.com
 *  Telegram        : https://t.me/dappincubatorofficial
 *  Concept         : High Return On Investment
 *  Category        : Passive Income
 *  Risk Category   : High Risk
 **/

pragma solidity >=0.6.0 <0.8.0;

abstract contract Ownable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

     /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor () internal {
        _owner = msg.sender;
        emit OwnershipTransferred(address(0), _owner);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(_owner == msg.sender, "Ownable: caller is not the owner");
        _;
    }
    
    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

abstract contract ReentrancyGuard {
    
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor () internal {
        _status = _NOT_ENTERED;
    }

    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

contract DappIncubator is Ownable, ReentrancyGuard {
    
    /**
     * @dev Structure to hold tokens supplu and dividend per share against collateral.
     */
    struct TokenLedger {
        uint supply;
        uint dividend;
        uint initialPrice;
        uint incrementPrice;
    }
    
    /**
     * @dev Structure to hold collateral balalnce of wallet.
     */
    struct BalanceLedger {
        uint tokenBalance;
        uint referralBalance;
        int payOut;
    }
    
    /**
     * @dev mapping to store all active contract addresses and wallet addresses. This will be used to check if contract address and wallet address already part of system.
     */
    mapping(address => bool) contractAddressRecord;
    mapping(address => bool) walletAddressRecord;
    
    uint constant magnitude = 1e18 ;
    uint constant dividendFee = 10;
    
    /**
     * @dev structure mapping created for storing balance and token information.
     */
    mapping (address => mapping(address => BalanceLedger)) balanceLedger;
    mapping(address => TokenLedger) tokenLedger;
    
    /**
     * @dev events to register information about collateral buy, sell, reinvest and token withdraw.
     */
     
    event onTokenOnboard(address indexed contractAddress, uint initialPrice, uint incrementPrice);
    event onPurchase(address indexed walletAddress, address indexed contractAddress, uint incomingTokenAmount, uint collateralMinted, address indexed referredBy);
    event onSell(address indexed walletAddress, address indexed contractAddress, uint tokenAmountToReceiver, uint collateralBurned);
    event onReinvest(address indexed walletAddress, address indexed contractAddress, uint reInvestTokenAmount, uint collateralMinted);
    event onWithdraw(address indexed walletAddress, address indexed contractAddress, uint amountToWithdraw);
    event onTransfer(address indexed contractAddress, address indexed from,address indexed to,uint256 tokens);
    
    /**
     * @dev function to add ERC-20 Token to ecosystem.
     */
     function tokenOnboard(address contractAddress, uint initialPrice, uint incrementPrice) public nonReentrant onlyOwner returns(bool)
     {
         if(contractAddressRecord[contractAddress] == false)
         {
            contractAddressRecord[contractAddress] = true;
            tokenLedger[contractAddress].initialPrice = initialPrice;
            tokenLedger[contractAddress].incrementPrice = incrementPrice;
            tokenLedger[contractAddress].supply = 0;
            tokenLedger[contractAddress].dividend = 0;
            
            emit onTokenOnboard(contractAddress, initialPrice, incrementPrice);
            
            return true;
         }
         
     }
    
    
    /**
     * @dev function to purchase collateral by sending Ethereum.
     */
    function buy(address _referredBy) public nonReentrant payable returns(uint256)
    {
        require(msg.value>0);
        require(contractAddressRecord[address(0)] == true);
        
        // if first investment from user then activate wallet address in system.
        if(walletAddressRecord[msg.sender] == false){
            walletAddressRecord[msg.sender] = true;
        }
        
        uint256 collateAmount = purchaseCollate(address(0), msg.value, _referredBy);
        return collateAmount;
    }