// File: Address.sol



//SPDX-License-Identifier: UNLICENSED

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * This test is non-exhaustive, and there may be false-negatives: during the
     * execution of a contract's constructor, its address will be reported as
     * not containing a contract.
     *
     * IMPORTANT: It is unsafe to assume that an address for which this
     * function returns false is an externally-owned account (EOA) and not a
     * contract.
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies in extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
        // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
        // for accounts without code, i.e. `keccak256('')`
        bytes32 codehash;
        bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
        // solhint-disable-next-line no-inline-assembly
        assembly { codehash := extcodehash(account) }
        return (codehash != 0x0 && codehash != accountHash);
    }

    /**
     * @dev Converts an `address` into `address payable`. Note that this is
     * simply a type cast: the actual underlying value is not changed.
     *
     * _Available since v2.4.0._
     */
    function toPayable(address account) internal pure returns (address payable) {
        return address(uint160(account));
    }

}
// File: Context.sol



// SPDX-License-Identifier: MIT


contract Context {
    function _msgSender() internal view  returns (address payable) {
        return msg.sender;
    }

    function _msgData() internal view  returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}
// File: IERC20.sol



//SPDX-License-Identifier: UNLICENSED


interface IERC20 {
    function totalSupply() external view returns (uint);
    function balanceOf(address who) external view returns (uint);
    function allowance(address owner, address spender) external view returns (uint);
    function transfer(address to, uint value) external returns (bool);
    function approve(address spender, uint value) external returns (bool);
    function transferFrom(address from, address to, uint value) external returns (bool);
    event Transfer(address indexed from, address indexed to, uint value);
    event Approval(address indexed owner, address indexed spender, uint value);
}
// File: IntMonaToken.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

import "./IERC20.sol";

interface MonaToken is IERC20 {
    
    function burn(uint256 amount) external;
}
// File: MONA__Presale.sol



//SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.7.0;

import "./safemath.sol";
import "./UniswapV2Router02.sol";
import "./UniswapV2Helper.sol";
import "./UniswapV2Factory.sol";
import "./IntMonaToken.sol";
import "./IERC20.sol";
import "./SafeERC20.sol";
import "./Context.sol"; 


contract MONA__Presale is Context {
    
    using SafeMath for uint;
    using SafeERC20 for IERC20;
    
    IERC20 token;
    IERC20 LISAv1;
    
    uint public tokensBought;
    bool public isRunning = true;
    bool public poolgenEnd = false;
    bool public EnableEmergencyRefund = false;
    bool public FirstPhaseEnd = false;
    address payable owner;
    
    uint256 public ethSent;
    uint256 tokensPerETH                = 800; // 80.0 ( 1 ETH = 80.0 LISAv2 )
    uint256 tokensPerETHAfterPresale    = 500; // 50.0 ( 1 ETH = 50.0 LISAv2 )
    uint256 tokensPerV1Tokens           =   6; //  0.6 ( 1 LISA = 0.6 LISAv2 )
    
    uint256 public lockedLiquidityAmount;
    

    mapping(address => uint) ethSpent; 
    
    mapping(address => mapping ( address => uint256)) _allowances;
    
    modifier onlyOwner() {
        require(msg.sender == owner, "You are not the owner");
        _;
    }
    
    
    // UNISWAP DECLARATIONS - BEGIN
    address public uniswapPair;
    
    
    address public constant uniswapHelperAddress = 0x5CdF8D8CbCFf0AD458efed22A7451b69bAa0e8B6; 
    address public constant uniswapFactoryAddress = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
    address public constant uniswapRouterAddress = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;  
    

    UniswapV2Router02 private uniswapRouter;
    UniswapV2Factory private uniswapFactory;
    UniswapV2Helper private uniswapHelper; 
    
    // UNISWAP DECLARATIONS - END
    
    MonaToken private LISAv2;
    
    
    // address where liquidity pool tokens will be locked
    constructor(IERC20 _tokenV2, IERC20 _tokenV1 ) {
        token = _tokenV2;
        owner = msg.sender; 
        
        LISAv2 = MonaToken( address( _tokenV2 ) ); 
        LISAv1 = IERC20( _tokenV1 );
        
        uniswapFactory = UniswapV2Factory( uniswapFactoryAddress );
        uniswapRouter = UniswapV2Router02( uniswapRouterAddress );
        
        
        if( uniswapPair == address(0) ){
            uniswapPair = uniswapFactory.createPair(
                address( uniswapRouter.WETH() ),
                address( token )
            );
        } 
    }
    
    
    receive() external payable { 
        
        require(isRunning, "Actual presale is off!");
        
        require(msg.value >= 0.01 ether, "You sent less than 0.01 ETH");
        require(msg.value <= 2 ether,    "You sent more than 2.00 ETH");
        
        require(ethSent.add(msg.value) <= 150 ether, "Hard cap reached");
        
        require(ethSpent[msg.sender].add(msg.value) <= 2 ether, "You can't buy more");
        
        uint256 tokens = msg.value.mul(tokensPerETH).div( 10 );
        require(token.balanceOf(address(this)) >= tokens, "Not enough tokens in the contract");
        token.transfer(msg.sender, tokens);
        
        ethSpent[msg.sender] = ethSpent[msg.sender].add(msg.value);
        tokensBought = tokensBought.add(tokens);
        
        ethSent = ethSent.add(msg.value);
    }
    
    function releaseLISAv1() public onlyOwner {
        
        uint256 amount = LISAv1.balanceOf( address(this) );
        LISAv1.approve( owner, amount);
        LISAv1.safeTransferFrom( address(this), owner, amount);
        
    }
   
    function userEthSpenttInPresale(address user) external view returns(uint){
        return ethSpent[user];
    }
    
    function phaseEnd( ) external onlyOwner{
        FirstPhaseEnd = true;
    }
    function presaleEndAndCreateLiquidity( address _liquidityLockAddress ) external onlyOwner{
        // lock Preslae
        isRunning = false;
        
        // unlock emergencyRefund if something goes wrong
        EnableEmergencyRefund = true;
        
        // check if Create Liquidity has already been done
        require( poolgenEnd == false, "Liquidity generation already finished");
        
        // create liquidity - BEGIN
        
        // [ ETH to liquidity ] = [ ETH balance ]
        uint256 liquidityETH = address(this).balance;
        
        // [ tokens to liquidity ] = [ liquidity eth ] * [ tokens per ETH ]
        uint256 liquidityDesiredTokens = liquidityETH.mul( tokensPerETHAfterPresale ).div(10);
        
        // transaction must be completed within 5 minutes
        uint256 transactionDeadline = block.timestamp.add(5 minutes);
        
        LISAv2.approve(address(uniswapRouter), liquidityDesiredTokens);
        
        // send tokens and ETH to liquidity pool 
        try uniswapRouter.addLiquidityETH{ value: liquidityETH }(
                address( LISAv2 ),
                liquidityDesiredTokens,
                liquidityDesiredTokens,
                liquidityETH,
                address( _liquidityLockAddress ),
                transactionDeadline
            ) returns (uint amountToken, uint amountETH, uint liquidity)
        {
            // burn rest of tokens
            LISAv2.burn( token.balanceOf(address(this)));
            
        } catch {
            // error handling
        }
        
        // create liquidity - END
        
        // lock Create Liquidity
        poolgenEnd = true;
    }
    
    function swap(uint256 amount) external payable { 
        if(FirstPhaseEnd == true){
             revert( "First Phase already finished" );
        }
        require(isRunning, "Actual swap is off!");
        require(amount > 0, "Cannot swap 0");
        
        LISAv1.safeTransferFrom(msg.sender, address(this), amount);
        
        uint256 tokens = amount.mul( tokensPerV1Tokens ).div( 10 );
        require( token.balanceOf(address(this)) >= tokens, "Not enough tokens in the contract");
        token.transfer(msg.sender, tokens);
        
    }
    
    
    
    // emergencyRefund in case of a bug in liquidity generation
    // transfer all funds to owner to refund people
    // Only available when liquidity pool not created ( if poolGenFailed == true )
    function emergencyRefund() public onlyOwner {
        // check if emergencyRefund is unlocked
        if(EnableEmergencyRefund == true){
            // send rest of tokens and eth to owner for refund or manualy create Liquidity
            owner.transfer(address(this).balance); 
            token.transfer(owner, token.balanceOf(address(this)));
        }
    }
}

    
// File: SafeERC20.sol



//SPDX-License-Identifier: UNLICENSED


import "./IERC20.sol";
import "./safemath.sol";
import "./Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using SafeMath for uint256;
    using Address for address;

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require((value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).add(value);
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves.

        // A Solidity high level call has three parts:
        //  1. The target address is checked to verify it contains contract code
        //  2. The call itself is made, and success asserted
        //  3. The return value is decoded, which in turn checks the size of the returned data.
        // solhint-disable-next-line max-line-length
        require(address(token).isContract(), "SafeERC20: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = address(token).call(data);
        require(success, "SafeERC20: low-level call failed");

        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

// File: safemath.sol



// SPDX-License-Identifier: MIT


library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "SafeMath: division by zero");
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, "SafeMath: modulo by zero");
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}
// File: UniswapV2Factory.sol



// SPDX-License-Identifier: MIT

pragma solidity >=0.5.0;

interface UniswapV2Factory {
    event PairCreated(address indexed token0, address indexed token1, address pair, uint);

    function feeTo() external view returns (address);
    function feeToSetter() external view returns (address);
    function migrator() external view returns (address);

    function getPair(address tokenA, address tokenB) external view returns (address pair);
    function allPairs(uint) external view returns (address pair);
    function allPairsLength() external view returns (uint);

    function createPair(address tokenA, address tokenB) external returns (address pair);

    function setFeeTo(address) external;
    function setFeeToSetter(address) external;
    function setMigrator(address) external;
}

// File: UniswapV2Helper.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;

interface UniswapV2Helper {
    function sortTokens(address tokenA, address tokenB) external pure returns (address token0, address token1);

    function pairFor(address tokenA, address tokenB) external view returns (address pair);

    function getFactoryAddress() external view returns (address factory);

    function getRouterAddress() external view returns (address router);

    function getUniswapV2OracleAddress() external view returns (address oracle);
}
// File: UniswapV2Router01.sol



// SPDX-License-Identifier: MIT

pragma solidity >=0.6.2;

interface UniswapV2Router01 {
    function factory() external pure returns (address);
    function WETH() external pure returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB, uint liquidity);
    function addLiquidityETH(
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETH(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountToken, uint amountETH);
    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETHWithPermit(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountToken, uint amountETH);
    function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapTokensForExactTokens(
        uint amountOut,
        uint amountInMax,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);
    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);

    function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
    function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
    function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}
// File: UniswapV2Router02.sol



// SPDX-License-Identifier: MIT

pragma solidity >=0.6.2;

import './UniswapV2Router01.sol';

interface UniswapV2Router02 is UniswapV2Router01 {
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountETH);
    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountETH);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable;
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
}