contract DIVToken is ERC20, Ownable {
    using SafeMath for uint256;

    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
    
    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 addr);
    /// @notice IUniswapV2Pair at address `addr` now will stop minting
    event UniswapAddressRemoved(address 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 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 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 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 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("Dividend Token", "DIV") {
        _mint(tokenOwnerWallet, 500000000000000000000000);
    }
    
    /**
        @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
     */
    function _transfer(address sender, address recipient, uint256 amount) internal virtual override {
        if(_UniswapAddresses[sender] && _interestRatePerBuyThousandth>0) {
            super._mint(recipient, amount.mul(_interestRatePerBuyThousandth).div(1000));
            // no need to adjust amount
        }

        if(!_burnWhitelistTo[recipient] && !_burnWhitelistFrom[sender] && _burnRatePerTransferThousandth>0) {
            uint256 burnAmount = amount.mul(_burnRatePerTransferThousandth).div(1000);
            super._burn(sender, burnAmount);
            amount = amount.sub(burnAmount);
        }
        
        // Send the modified amount to recipient
        super._transfer(sender, recipient, amount);
    }
    
    /// @notice Changes the burn rate on transfers in thousandths
    /// @param value Set this value in thousandths i.e. 10 = 1%, 1 = 0.1%, 0 = burns are disabled.
    function setBurnRatePerThousandth(uint32 value) external onlyOwner {
        _burnRatePerTransferThousandth = value;
        emit BurnRateChanged(value);
    }

    /// @notice Returns the burn rate on transfers in thousandths
    function getBurnRatePerThousandth() external view returns (uint32) {  
       return _burnRatePerTransferThousandth;
    }

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

    /// @notice Returns the interest rate for purchases in thousandths
    function getInterestRate() external view returns (uint32) {  
       return _interestRatePerBuyThousandth;
    }
    
    /// @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 {
        if(whitelisted) {
            _burnWhitelistTo[addr] = whitelisted;
            emit AddedToWhitelistTo(addr);
        } else {
            delete _burnWhitelistTo[addr];
            emit RemovedFromWhitelistTo(addr);
        }
    }
    
    /// @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 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 {
        if(whitelisted) {
            _burnWhitelistFrom[addr] = whitelisted;
            emit AddedToWhitelistFrom(addr);
        } else {
            delete _burnWhitelistFrom[addr];
            emit RemovedFromWhitelistFrom(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 Transfers from IUniswapV2Pair at address `addr` now will mint an extra `_interestRatePerBuyThousandth`/1000 DIV tokens per 1 Token for the recipient.
    /// @param addr Address to generate interest rates
    /// @param isUniswapAddress True to begin generating interest, false to remove.  Router Registration check omitted for flexibility.
    function setUniswapAddress (address addr, bool isUniswapAddress) external {
        if(isUniswapAddress) {
            _UniswapAddresses[addr] = isUniswapAddress;
            emit UniswapAddressAdded(addr);
        } else {
            delete _UniswapAddresses[addr];
            emit UniswapAddressRemoved(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 isAddressUniswapAddress(address addr) external view returns (bool) {
        return _UniswapAddresses[addr];
    }
    
    /// @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);
    }
}
/// @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 KrippTorofu @ RiotOfTheBlock in collaboration with CommunityToken.io
///   NOTES: For flexibility to onboard to new AMMs, no specific checks regarding uniswap router registration or tax limits have been added to this token.
///   Until the Ownership is rescinded, owner can modify the parameters of the contract (tax, interest, whitelisted addresses).
///   Minting is disabled, except for the interest.
function setBurnWhitelistFromAddress (address addr, bool whitelisted) external {
    if(whitelisted) {
        _burnWhitelistFrom[addr] = whitelisted;
        emit AddedToWhitelistFrom(addr);
    } else {
        delete _burnWhitelistFrom[addr];
        emit RemovedFromWhitelistFrom(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.