contract CRBSale is Context, Ownable, ReentrancyGuard {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    /* ---------- VARIABLES ---------- */

    // The token being sold
    IERC20 public token;

    // USDT instance
    IERC20 public usdt;

    // Addresses where funds are collected
    address public firstWallet;
    address public secondWallet;

    // How many token units a buyer gets per wei
    uint256 public ethRate;
    uint256 public usdtRate;

    // Duration of Sale
    uint256 public duration;

    // Starting date of Sale
    uint256 public startDate;

    // Ending date of Sale
    uint256 public endDate;

    // Minimum amount of investment a user can make
    uint256 public minEthInvestment;
    uint256 public minUsdtInvestment;

    // Total amount raised
    uint256 public weiRaised;

    // Buy type
    enum BuyType {
        ETH,
        USDT
    }

    /* ---------- EVENTS ---------- */

    /**
     * Event for token purchase logging
     * @param beneficiary who got the tokens
     * @param value weis paid for purchase
     * @param amount amount of tokens purchased
     */
    event TokensPurchased(
        address indexed beneficiary,
        uint256 value,
        uint256 amount
    );

    /**
     * Event for withdraw of all tokens
     * @param beneficiary who got the tokens (owner)
     * @param amount amount of tokens purchased
     */
    event TokensWithdrawn(
        address indexed beneficiary,
        uint256 amount
    );

    /**
     * Event for withdraw of all tokens
     * @param walletAddress who got the tokens (owner)
     * @param amount amount of tokens purchased
     */
    event FundsForward(
        address indexed walletAddress,
        uint256 amount
    );

    /**
     * Event when owner changes eth rate
     * @param prevRate previous rate of usdt
     * @param newRate new rate of usdt
     */
    event ETHRateChanged(
        uint256 prevRate,
        uint256 newRate
    );

    /**
     * Event when owner changes usdt rate
     * @param prevRate previous rate of usdt
     * @param newRate new rate of usdt
     */
    event USDTRateChanged(
        uint256 prevRate,
        uint256 newRate
    );

    /* ---------- CONSTRUCTOR ---------- */

    /**
     * @param _firstWallet Address where half funds are collected
     * @param _secondWallet Address where half funds are collected
     * @param _usdt Address of USDT
     * @param _token Address of the token being sold
     */
    constructor (
        address _firstWallet,
        address _secondWallet,
        IERC20 _usdt,
        IERC20 _token,
        uint256 _ethRate,
        uint256 _usdtRate,
        uint256 _duration,
        uint256 _startDate,
        uint256 _minEthInvestment,
        uint256 _minUsdtInvestment
    ) {
        require(address(_usdt) != address(0), "CRBSale: usdt cannot be zero address");
        require(address(_token) != address(0), "CRBSale: token cannot be zero address");

        firstWallet = _firstWallet;
        secondWallet = _secondWallet;
        usdt = _usdt;
        token = _token;

        ethRate = _ethRate;
        usdtRate = _usdtRate;
        duration = _duration;
        startDate = _startDate;
        endDate = _startDate.add(_duration);
        minEthInvestment = _minEthInvestment;
        minUsdtInvestment = _minUsdtInvestment;
    }

    /* ---------- PUBLIC FUNCTIONS ---------- */

    /**
     * @dev fallback function ***DO NOT OVERRIDE***
     * Note that other contracts will transfer funds with a base gas stipend
     * of 2300, which is not enough to call buyTokens. Consider calling
     * buyTokens directly when purchasing tokens from a contract.
     */
    receive () external payable {
        buyWithETH();
    }
    
    function buyWithETH() public payable {
        require(msg.value >= minEthInvestment, "CRBSale: weiAmount not valid");
        _buyTokens(msg.value, BuyType.ETH);
    }
    
    function buyWithUSDT(uint256 _weiAmount) public {
        require(_weiAmount >= minUsdtInvestment, "CRBSale: weiAmount not valid");
        _buyTokens(_weiAmount, BuyType.USDT);
    }

    /* ---------- INTERNAL FUNCTIONS ---------- */

    /**
     * @dev low level token purchase ***DO NOT OVERRIDE***
     * This function has a non-reentrancy guard, so it shouldn't be called by
     * another `nonReentrant` function.
     * @param _weiAmount Value in wei to be converted into tokens
     */
    function _buyTokens(uint256 _weiAmount, BuyType _buyType) internal nonReentrant {
        address _beneficiary = _msgSender();

        _preValidatePurchase(_beneficiary, _weiAmount);
        _recieveUsdt(_beneficiary, _weiAmount, _buyType);

        // calculate token amount to be created
        uint256 tokens = _getTokenAmount(_weiAmount, _buyType);

        // update state
        weiRaised = weiRaised.add(_weiAmount);

        _processPurchase(_beneficiary, tokens);
        emit TokensPurchased(_beneficiary, _weiAmount, tokens);

        _forwardFunds(_weiAmount, _buyType);
    }

    /**
     * @dev Validation of an incoming purchase. Use require statements to revert state
     * when conditions are not met.
     * @param _beneficiary Address performing the token purchase
     * @param _weiAmount Value in wei involved in the purchase
     */
    function _preValidatePurchase(address _beneficiary, uint256 _weiAmount) internal view {
        require(_beneficiary != address(0), "CRBSale: beneficiary is the zero address");
        require(_weiAmount != 0, "CRBSale: weiAmount is 0");
        require(
            block.timestamp >= startDate && block.timestamp <= endDate,
            "CRBSale: Sale period is ended"
        );
    }

    /**
     * @dev Source of tokens. Override this method to modify the way in which the crowdsale
     * ultimately gets and sends its tokens.
     * @param _beneficiary Address performing the token purchase
     * @param _weiAmount Number of tokens to be received
     */
    function _recieveUsdt(
        address _beneficiary,
        uint256 _weiAmount,
        BuyType _buyType
    ) internal {
        if (_buyType == BuyType.USDT) {
            usdt.safeTransferFrom(_beneficiary, address(this), _weiAmount);
        }
    }

    /**
     * @dev Executed when a purchase has been validated and is ready to be executed. 
     * Doesn't necessarily emit/send tokens.
     * @param _beneficiary Address receiving the tokens
     * @param _tokenAmount Number of tokens to be purchased
     */
    function _processPurchase(address _beneficiary, uint256 _tokenAmount) internal {
        // deliverTokens
        token.safeTransfer(_beneficiary, _tokenAmount);
    }

    /**
     * @param _weiAmount Value in wei to be converted into tokens
     * @return Number of tokens that can be purchased with the specified _weiAmount
     */
    function _getTokenAmount(
        uint256 _weiAmount,
        BuyType _buyType
    )
        internal
        view
        returns (uint256)
    {
        if (_buyType == BuyType.ETH) {
            return _weiAmount.div(ethRate).mul(10**18);
        } else {
            return _weiAmount.div(usdtRate).mul(10**18);
        }
    }

    /**
     * @dev USDT is forwarded on purchases
     */
    function _forwardFunds(uint256 _weiAmount, BuyType _buyType) internal {
        uint256 _amount = _weiAmount/2;

        if (_buyType == BuyType.ETH) {
            payable(firstWallet).transfer(_amount);

            payable(secondWallet).transfer(_amount);
        } else {
            usdt.safeTransfer(firstWallet, _amount);
            usdt.safeTransfer(secondWallet, _amount);
        }

        emit FundsForward(firstWallet, _amount);
        emit FundsForward(secondWallet, _amount);
    }

    /* ---------- ONLY-OWNER FUNCTIONS ---------- */

    function withdrawCRBTokens() public onlyOwner() {
        uint256 _amount = token.balanceOf(address(this));
        token.transfer(msg.sender, _amount);
        emit TokensWithdrawn(msg.sender, _amount);
    }

    function setEthRate(uint256 _ethRate) public onlyOwner() {
        uint256 _preEthRate = ethRate;
        ethRate = _ethRate;
        emit ETHRateChanged(_preEthRate, _ethRate);
    }

    function setUsdtRate(uint256 _usdtRate) public onlyOwner() {
        uint256 _preUsdtRate = usdtRate;
        usdtRate = _usdtRate;
        emit USDTRateChanged(_preUsdtRate, _usdtRate);
    }
}
// import "hardhat/console.sol";
function withdrawCRBTokens() public onlyOwner() {
    uint256 _amount = token.balanceOf(address(this));
    token.transfer(msg.sender, _amount);
    emit TokensWithdrawn(msg.sender, _amount);
}

/* ---------- ONLY-OWNER FUNCTIONS ---------- */