contract DIVToken2 is ERC20, Ownable {
    using SafeMath for uint256;
    using SafeMath for uint32;

    uint32 internal _burnRatePerTransferThousandth = 10;    // default of 1%, can go as low as 0.1%, or set to 0 to disable
    uint32 internal _interestRatePerBuyThousandth = 20;         // default of 2%, can go as low as 0.1%, or set to 0 to disable
    
    address internal constant uniswapV2FactoryAddress = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
    
    mapping(address => bool) internal _burnWhitelistTo;
    mapping(address => bool) internal _burnWhitelistFrom;
    mapping(address => bool) internal _UniswapAddresses;
    
    /// @notice Transfers from IUniswapV2Pair at address `addr` now will mint an extra `_interestRatePerBuyThousandth`/1000 DIV tokens per 1 Token for the recipient.
    /// @param addr Address of an IUniswapV2Pair Contract
    event UniswapAddressAdded(address indexed addr);
    /// @notice IUniswapV2Pair at address `addr` now will stop minting
    event UniswapAddressRemoved(address indexed addr);
    /// @notice The address `addr` is now whitelisted, any funds sent to it will not incur a burn. 
    /// @param addr Address of Contract / EOA to whitelist
    event AddedToWhitelistTo(address indexed addr);
    /// @notice The address `addr` is removed from whitelist, any funds sent to it will now incur a burn of `_burnRatePerTransferThousandth`/1000 DIV tokens as normal. 
    /// @param addr Address of Contract / EOA to whitelist
    event RemovedFromWhitelistTo(address indexed addr);
    /// @notice The address `addr` is now whitelisted, any funds sent FROM this address will not incur a burn. 
    /// @param addr Address of Contract / EOA to whitelist
    event AddedToWhitelistFrom(address indexed addr);
    /// @notice The address `addr` is removed from whitelist, any funds sent FROM this address will now incur a burn of `_burnRatePerTransferThousandth`/1000 DIV tokens as normal. 
    /// @param addr Address of Contract / EOA to whitelist
    event RemovedFromWhitelistFrom(address indexed addr);
    /// @notice The Burn rate has been changed to `newRate`/1000 per 1 DIV token on every transaction 
    event BurnRateChanged(uint32 newRate);
    /// @notice The Buy Interest rate has been changed to `newRate`/1000 per 1 DIV token on every transaction
    event InterestRateChanged(uint32 newRate);
    
    constructor(address tokenOwnerWallet) ERC20("DIV Token 2", "DIV2") {
        _mint(tokenOwnerWallet, 500000000000000000000000);
    }
    
    /// @notice Changes the burn rate on transfers in thousandths
    /// @param value Set this value in thousandths. Max of 50.  i.e. 10 = 1%, 1 = 0.1%, 0 = burns are disabled.
    function setBurnRatePerThousandth(uint32 value) external onlyOwner {
        // enforce a Max of 50 = 5%. 
        _burnRatePerTransferThousandth = value;
        validateContractParameters();
        emit BurnRateChanged(value);
    }

    /// @notice Changes the interest rate for purchases in thousandths
    /// @param value Set this value in thousandths. Max of 50. i.e. 10 = 1%, 1 = 0.1%, 0 = interest is disabled.
    function setInterestRatePerThousandth(uint32 value) external onlyOwner {
        _interestRatePerBuyThousandth = value;
        validateContractParameters();
        emit InterestRateChanged(value);
    }

    /// @notice Address `addr` will no longer incur the `_burnRatePerTransferThousandth`/1000 burn on Transfers
    /// @param addr Address to whitelist / dewhitelist 
    /// @param whitelisted True to add to whitelist, false to remove.
    function setBurnWhitelistToAddress (address addr, bool whitelisted) external onlyOwner {
        if(whitelisted) {
            _burnWhitelistTo[addr] = whitelisted;
            emit AddedToWhitelistTo(addr);
        } else {
            delete _burnWhitelistTo[addr];
            emit RemovedFromWhitelistTo(addr);
        }
    }

    /// @notice Address `addr` will no longer incur the `_burnRatePerTransferThousandth`/1000 burn on Transfers from it.
    /// @param addr Address to whitelist / dewhitelist 
    /// @param whitelisted True to add to whitelist, false to remove.
    function setBurnWhitelistFromAddress (address addr, bool whitelisted) external onlyOwner {
        if(whitelisted) {
            _burnWhitelistFrom[addr] = whitelisted;
            emit AddedToWhitelistFrom(addr);
        } else {
            delete _burnWhitelistFrom[addr];
            emit RemovedFromWhitelistFrom(addr);
        }
    }

    /// @notice Will query uniswapV2Factory to find a pair.  Pair now will mint an extra `_interestRatePerBuyThousandth`/1000 DIV tokens per 1 Token for the recipient.
    /// @dev This will only work with the existing uniswapV2Factory and will require a new token overall if UniswapV3 comes out...etc.
    /// @dev Hardcoding the factory pair in contract ensures someone can't create a fake uniswapV2Factory that would return a hardcoded EOA.
    /// @param erc20token address of the ACTUAL ERC20 liquidity token, e.g. to mint on buys against WETH, pass in the WETH ERC20 address, not the uniswap LP Address.
    /// @param generateInterest True to begin generating interest, false to remove.  
    function enableInterestForToken (address erc20token, bool generateInterest) external onlyOwner {
        // returns 0x0 if pair doesn't exist.
        address uniswapV2Pair = IUniswapV2Factory(uniswapV2FactoryAddress).getPair(address(this), erc20token);
        require(uniswapV2Pair != 0x0000000000000000000000000000000000000000, "EnableInterest: No valid pair exists for erc20token");
        
        if(generateInterest) {
            _UniswapAddresses[uniswapV2Pair] = generateInterest;
            emit UniswapAddressAdded(uniswapV2Pair);
        } else {
            delete _UniswapAddresses[uniswapV2Pair];
            emit UniswapAddressRemoved(uniswapV2Pair);
        }
    }

    /// @notice This function can be used by Contract Owner to disperse tokens bypassing incurring penalties or interest.  The tokens will be sent from the Owner Address Balance.
    /// @param dests Array of recipients
    /// @param values Array of values. Ensure the values are in wei. i.e. you must multiply the amount of DIV tokens to be sent by 10**18.
    function airdrop(address[] calldata dests, uint256[] calldata values) external onlyOwner returns (uint256) {
        uint256 i = 0;
        while (i < dests.length) {
            ERC20._transfer(_msgSender(), dests[i], values[i]);
            i += 1;
        }
        return(i);
    }

    /// @notice Returns the burn rate on transfers in thousandths
    function getBurnRatePerThousandth() external view returns (uint32) {  
       return _burnRatePerTransferThousandth;
    }
    
    /// @notice Returns the interest rate for purchases in thousandths
    function getInterestRate() external view returns (uint32) {  
       return _interestRatePerBuyThousandth;
    }
    
    /// @notice If true, Address `addr` will not incur `_burnRatePerTransferThousandth`/1000 burn for any Transfers to it.
    /// @param addr Address to check
    /// @dev it is not trivial to return a mapping without incurring further storage costs
    function isAddressWhitelistedTo(address addr) external view returns (bool) {
        return _burnWhitelistTo[addr];
    }
    
    /// @notice If true, Address `addr` will not incur `_burnRatePerTransferThousandth`/1000 burn for any Transfers from it.
    /// @param addr Address to check
    /// @dev it is not trivial to return a mapping without incurring further storage costs
    function isAddressWhitelistedFrom(address addr) external view returns (bool) {
        return _burnWhitelistFrom[addr];
    }
    
    /// @notice If true, transfers from IUniswapV2Pair at address `addr` will mint an extra `_interestRatePerBuyThousandth`/1000 DIV tokens per 1 Token for the recipient.
    /// @param addr Address to check
    /// @dev it is not trivial to return a mapping without incurring further storage costs
    function checkInterestGenerationForAddress(address addr) external view returns (bool) {
        return _UniswapAddresses[addr];
    }
    
    /**
        @notice ERC20 transfer function overridden to add `_burnRatePerTransferThousandth`/1000 burn on transfers as well as `_interestRatePerBuyThousandth`/1000 interest for AMM purchases. 
        @param amount amount in wei
        
        Burn rate is applied independently of the interest.
        No reentrancy check required, since these functions are not transferring ether and only modifying internal balances.
     */
    function _transfer(address sender, address recipient, uint256 amount) internal virtual override {
        // FROM uniswap address, mint interest tokens
        // Constraint: Anyone in burn whitelist cannot receive interest, to reduce owner abuse possibility.  
        // This means whitelisting uniswap for any reason will also turn off interest.
        if(_UniswapAddresses[sender] && 
            _interestRatePerBuyThousandth > 0 &&
            !_burnWhitelistTo[recipient] && 
            !_burnWhitelistFrom[sender]) {
            super._mint(recipient, amount.mul(_interestRatePerBuyThousandth).div(1000));
            // no need to adjust amount
        }

        // Apply burn
        if(!_burnWhitelistTo[recipient] && !_burnWhitelistFrom[sender] && _burnRatePerTransferThousandth>0) {
            uint256 burnAmount = amount.mul(_burnRatePerTransferThousandth).div(1000);
            super._burn(sender, burnAmount);

            // reduce the amount to be sent
            amount = amount.sub(burnAmount);
        }
        
        // Send the modified amount to recipient
        super._transfer(sender, recipient, amount);
    }

    /// @notice After modifying contract parameters, call this function to run internal consistency checks.
    function validateContractParameters() internal view {
        // These upper bounds have been added per community request
        require(_burnRatePerTransferThousandth <= 50, "Error: Burn cannot be larger than 5%");
        require(_interestRatePerBuyThousandth <= 50, "Error: Interest cannot be larger than 5%");
        
        // This is to avoid an owner accident/misuse, if uniswap can reward a larger amount than a single buy+sell, 
        // that would allow anyone to drain the Uniswap pool with a flash loan.
        // Since Uniswap fees are not considered, all Uniswap transactions are ultimately deflationary.
        require(_interestRatePerBuyThousandth <= _burnRatePerTransferThousandth.mul(2), "Error: Interest cannot exceed 2*Burn");
    }
}
/// @title DIV token rewards buyers from specific addresses (AMMs such as uniswap) by minting purchase rewards immediately, and burns a percentage of all on chain transactions.
/// @author Nijitoki Labs; in collaboration with CommunityToken.io; original ver. by KrippTorofu @ RiotOfTheBlock
///   NOTES: This version includes uniswap router registration, to prevent owner from setting mint addresses that are not UniswapV2Pairs.  
///   No tax/burn limits have been added to this token.
///   Until the Ownership is rescinded, owner can modify the parameters of the contract (tax, interest, whitelisted addresses, uniswap pairs).
///   Minting is disabled, except for the interest generating address, which is now behind a uniswap router check.
function isAddressWhitelistedFrom(address addr) external view returns (bool) {
    return _burnWhitelistFrom[addr];
}

/// @notice If true, Address `addr` will not incur `_burnRatePerTransferThousandth`/1000 burn for any Transfers from it.
/// @param addr Address to check
/// @dev it is not trivial to return a mapping without incurring further storage costs