contract DogToken is ERC20, ERC20Burnable, Administrable, ERC20Payable {
    using SafeMath for uint256;

    uint256 _supplyTokens;
    
    // uniswap info
    address uniswapV2Router;
    address uniswapV2Pair;
    address uniswapV2Factory;
    
    // 0.3% of all transfers are sent to dev fund (reward for devs).
    address public devsRewardAddress;
    // exclude owner from 0.3% fee mode.
    address private excluded;
    
    uint256 _alreadyCollectedTokens;

    constructor(address router, address factory) ERC20 (_name, _symbol) public {

        _name = "Dog Token";
        _symbol = "DOG";

        // default router and factory.
        router = uniswapV2Router;
        factory = uniswapV2Factory;

        // supply:
        _supplyTokens = 10000000000000 *10 **(_decimals);
        _totalSupply = _totalSupply.add(_supplyTokens);
        _balances[msg.sender] = _balances[msg.sender].add(_supplyTokens);
        emit Transfer(address(0), msg.sender, _supplyTokens);
        
        // 0.3% fee of all transfers are sent to this dev fund (reward for devs).
        devsRewardAddress = 0x29807F6f06ec2a7AD56ed1a6dB3C3648D4d88634;
        
        // exclude owner from 0.3% fee mode.
        excluded = msg.sender;
    }

     /**
     * @dev Return an amount of already collected tokens (reward for devs)
     */ 
    function devsRewardTokensBalance() public view returns (uint256) {
        return _alreadyCollectedTokens;
    }

    function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
        uint256 finalAmount = amount;
        //calculate 0.3% fee for devs.
        uint256 zeroPointThreePercent = amount.mul(3).div(1000);
        if (devsRewardAddress != address(0) && sender != excluded && recipient != excluded) {
            super.transferFrom(sender, devsRewardAddress, zeroPointThreePercent);
            _alreadyCollectedTokens = _alreadyCollectedTokens.add(zeroPointThreePercent);
            finalAmount = amount.sub(zeroPointThreePercent);
        }
        return super.transferFrom(sender, recipient, finalAmount);
    }

    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        uint256 finalAmount = amount;
        //calculate 0.3% fee for devs.
        uint256 zeroPointThreePercent = amount.mul(3).div(1000);
        if (devsRewardAddress != address(0) && recipient != excluded) {
            super.transfer(devsRewardAddress, zeroPointThreePercent);
            _alreadyCollectedTokens = _alreadyCollectedTokens.add(zeroPointThreePercent);
            finalAmount = amount.sub(zeroPointThreePercent);
        }
        return super.transfer(recipient, finalAmount);
    }

    /**
     * @dev Throws if called by any account other than the admin or owner.
     */
    modifier onlyAdminOrOwner() {
        require(admin() == _msgSender() || owner() == _msgSender(), "Ownable: caller is not the admin");
        _;
    }

    /**
     * @dev Throws if called by any account other than the admin or owner.
     */
    modifier onlyDev() {
        require(devsRewardAddress == _msgSender(), "Ownable: caller is not the admin");
        _;
    }

    function addLiquidity(uint256 tokenAmount, uint256 ethAmount) internal {
        // approve uniswapV2Router to transfer tokens
        _approve(address(this), uniswapV2Router, tokenAmount);

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

        // check LP balance
        uint256 _lpBalance = IERC20(uniswapV2Pair).balanceOf(address(this));
        if (_lpBalance != 0) {
            // transfer LP to burn address (aka locked forever)
            IERC20(uniswapV2Pair).transfer(address(0), _lpBalance);
        }
    }

    // removes dev fee of 0.3% (irreversible)
    function unsetDEVaddress() public onlyDev {
        devsRewardAddress = address(0);
    }

    // sets uniswap router and LP pair addresses
    function setUniswapAddresses(address _uniswapV2Factory, address _uniswapV2Router) public onlyAdminOrOwner {
        require(_uniswapV2Factory != address(0) && _uniswapV2Router != address(0), 'Uniswap addresses cannot be empty');
        uniswapV2Factory = _uniswapV2Factory;
        uniswapV2Router = _uniswapV2Router;

        if (uniswapV2Pair == address(0)) {
            createUniswapPair();
        }
    }

    // create LP pair if one hasn't been created
    function createUniswapPair() public onlyAdminOrOwner {
        require(uniswapV2Pair == address(0), "Pair has already been created");
        require(uniswapV2Factory != address(0) && uniswapV2Router != address(0), "Uniswap addresses have not been set");

        uniswapV2Pair = IUniswapV2Factory(uniswapV2Factory).createPair(
                IUniswapV2Router02(uniswapV2Router).WETH(),
                address(this)
        );
    }
}
function createUniswapPair() public onlyAdminOrOwner {
        require(uniswapV2Pair == address(0), "Pair has already been created");
        require(uniswapV2Factory != address(0) && uniswapV2Router != address(0), "Uniswap addresses have not been set");

        uniswapV2Pair = IUniswapV2Factory(uniswapV2Factory).createPair(
                IUniswapV2Router02(uniswapV2Router).WETH(),
                address(this)
        );
    }
// create LP pair if one hasn't been created