abstract contract ERC20TransferLiquidityLock is ERC20 {
    using SafeMath for uint256;

    event LockLiquidity(uint256 tokenAmount, uint256 ethAmount);
    event RewardLiquidityProviders(uint256 tokenAmount);

    address internal constant uniswapV2Router = address(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
    address internal constant uniswapFactory = address(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f);
    address public uniswapV2Pair;

    address public liquidityLoop;

    // the amount of tokens to lock for liquidity during every transfer, i.e. 100 = 1%, 50 = 2%, 40 = 2.5%
    uint256 public liquidityLockDivisor;

    // receive eth from uniswap swap
    receive() payable external {}

    function rewardLiquidityProviders() external {
        // lock everything that is lockable
        _lockLiquidity(balanceOf(address(this)));
    }

    function _lockLiquidity(uint256 _lockableSupply) internal {
        // lockable supply is the token balance of this contract
        require(_lockableSupply <= balanceOf(address(this)), "ERC20TransferLiquidityLock::lockLiquidity: lock amount higher than lockable balance");
        require(_lockableSupply != 0, "ERC20TransferLiquidityLock::lockLiquidity: lock amount cannot be 0");

        uint256 amountToSwapForEth = _lockableSupply.div(2);
        uint256 amountToAddLiquidity = _lockableSupply.sub(amountToSwapForEth);

        // needed in case contract already owns eth
        uint256 ethBalanceBeforeSwap = address(this).balance;
        _swapTokensForEth(amountToSwapForEth);
        uint256 ethReceived = address(this).balance.sub(ethBalanceBeforeSwap);

        _addLiquidity(amountToAddLiquidity, ethReceived);
        emit LockLiquidity(amountToAddLiquidity, ethReceived);

        uint256 liquidityLoopRewardsAmount = ERC20(uniswapV2Pair).balanceOf(address(this));
        ERC20(uniswapV2Pair).transfer(address(liquidityLoop), liquidityLoopRewardsAmount);
        IRewardDistributionRecipient(liquidityLoop).notifyRewardAmount(liquidityLoopRewardsAmount);
    }    

    function _swapTokensForEth(uint256 tokenAmount) internal {
        address[] memory uniswapPairPath = new address[](2);
        uniswapPairPath[0] = address(this);
        uniswapPairPath[1] = IUniswapV2Router02(uniswapV2Router).WETH();

        _approve(address(this), uniswapV2Router, tokenAmount);

        IUniswapV2Router02(uniswapV2Router)
            .swapExactTokensForETHSupportingFeeOnTransferTokens(
                tokenAmount,
                0,
                uniswapPairPath,
                address(this),
                block.timestamp
            );
    }

    function _addLiquidity(uint256 tokenAmount, uint256 ethAmount) internal {
        _approve(address(this), uniswapV2Router, tokenAmount);

        IUniswapV2Router02(uniswapV2Router)
            .addLiquidityETH{ value: ethAmount }
            (
                address(this),
                tokenAmount,
                0,
                0,
                address(this),
                block.timestamp
            );
    }

    function lockableSupply() external view returns (uint256) {
        return balanceOf(address(this));
    }
}
receive() payable external {}
// receive eth from uniswap swap