// File: @openzeppelin/contracts/access/Ownable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: @openzeppelin/contracts/utils/Context.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

// File: contracts/test.sol



//SPDX-License-Identifier: MIT

pragma solidity 0.8.19;

import "@openzeppelin/contracts/access/Ownable.sol";

struct Account {
    bool isGlobal;
    uint256 balance;
    uint256 globalRewardsPaid;
}

contract RewardToken is Ownable {
    string private _name;
    string private _symbol;
    uint8 private _decimals;
    uint256 private _totalSupply;
    uint8 private immutable _adminTax;
    uint8 private immutable _globalTax;
    uint8 private immutable _burnTax;
    address private immutable _adminTaxReceiver;
    address private immutable _burnAddress;

    address[] private _globalList;

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );
    event GlobalRewardPaid(address to, uint256 value);
    event AdminTaxPaid(address to, uint256 value);
    event Burn(address _burnAddress, uint256 value);
    event ExcludeStatus(address, bool);

    mapping(address => Account) private _accounts;
    mapping(address => mapping(address => uint256)) private _allowances;
    mapping(address => bool) private _isExcludedFromTax;

    constructor() {
        _name = "Trump Army";
        _symbol = "TRUMPX";
        _decimals = 18;
        _totalSupply = 100000000 * 10 ** _decimals;
        _adminTax = 2;
        _globalTax = 2;
        _burnTax = 1;
        _adminTaxReceiver = 0x1f6b32601345A92FB77656699ae157be9270D9B2;
        _burnAddress = 0x000000000000000000000000000000000000dEaD;

        _isExcludedFromTax[owner()] = true;

        _accounts[msg.sender].balance += _totalSupply;
        emit Transfer(address(0), msg.sender, _totalSupply);
    }

    function name() external view returns (string memory) {
        return _name;
    }

    function symbol() external view returns (string memory) {
        return _symbol;
    }

    function decimals() external view returns (uint8) {
        return _decimals;
    }

    function totalSupply() external view returns (uint256) {
        return _totalSupply;
    }

    function balanceOf(address _userAddress) external view returns (uint256) {
        return _accounts[_userAddress].balance;
    }

    function transfer(address to, uint256 amount) external {
        _transfer(msg.sender, to, amount);
    }

    function allowance(
        address owner,
        address spender
    ) external view returns (uint256) {
        return _allowances[owner][spender];
    }

    function approve(address spender, uint256 amount) external returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    function _approve(
        address _owner,
        address _spender,
        uint256 _valueInWei
    ) internal virtual {
        require(_owner != address(0), "ERC20: approve from the zero address");
        require(_spender != address(0), "ERC20: approve to the zero address");
        require(_valueInWei > 0, "ERC20: approve value is zero.");

        _allowances[_owner][_spender] = _valueInWei;
        emit Approval(_owner, _spender, _valueInWei);
    }

    function globalList() external view returns (address[] memory) {
        return _globalList;
    }

    function increaseAllowance(address _spender, uint256 _valueInWei) external {
        require(_valueInWei > 0, "ERC20: approve value is zero.");
        address _msgSender = msg.sender;
        require(
            _accounts[_msgSender].balance >=
                _allowances[_msgSender][_spender] + _valueInWei,
            "ERC20: Insufficient balance to approve."
        );
        _allowances[_msgSender][_spender] += _valueInWei;
    }

    function decreaseAllowance(address _spender, uint256 _valueInWei) external {
        require(_valueInWei > 0, "ERC20: approve value is zero.");
        require(
            _spender != address(0),
            "ERC20: spender can't be address zero."
        );

        address _msgSender = msg.sender;
        require(
            _allowances[_msgSender][_spender] - _valueInWei >= 0,
            "ERC20: Allowance cannot be less than zero."
        );
        _allowances[_msgSender][_spender] -= _valueInWei;
    }

    function transferFrom(address from, address to, uint256 amount) external {
        address _msgSender = msg.sender;
        _transferFrom(from, _msgSender, to, amount);
    }

    function _transfer(
        address _from,
        address _to,
        uint256 _valueInWei
    ) private {
        require(
            _to != address(0),
            "ERC20: Address zero cannot be the receiver."
        );
        require(_valueInWei > 0, "ERC20: Value should be greater than zero.");

        uint256 userBalance = _accounts[_from].balance;
        require(userBalance >= _valueInWei, "ERC20: Insuffient balance.");

        if (_isExcludedFromTax[_from] || _isExcludedFromTax[_to]) {
            unchecked {
                _accounts[_from].balance -= _valueInWei;
                _accounts[_to].balance += _valueInWei;
                emit Transfer(_from, _to, _valueInWei);
            }
        } else {
            Account storage toAccount = _accounts[_to];
            address burnAddress = _burnAddress;
            address adminTaxReceiver = _adminTaxReceiver;

            if (!toAccount.isGlobal) {
                _globalList.push(_to);
                toAccount.isGlobal = true;
            }

            unchecked {
                uint256 adminTaxValue = (_valueInWei * _adminTax) / 100;
                uint256 globalReward = (_valueInWei * _globalTax) / 100;
                uint256 burnTaxValue = (_valueInWei * _burnTax) / 100;

                uint256 remainingValue = _valueInWei -
                    (adminTaxValue + globalReward + burnTaxValue);

                _accounts[_from].balance -= _valueInWei;

                _accounts[adminTaxReceiver].balance += adminTaxValue;
                emit AdminTaxPaid(adminTaxReceiver, adminTaxValue);

                _accounts[burnAddress].balance += burnTaxValue;
                emit Burn(burnAddress, burnTaxValue);

                toAccount.balance += remainingValue;

                address globalAddress = _getRandomGlobalAddress();
                _accounts[globalAddress].balance += globalReward;
                emit GlobalRewardPaid(globalAddress, globalReward);

                emit Transfer(_from, _to, remainingValue);
                emit Transfer(_from, adminTaxReceiver, adminTaxValue);
                emit Transfer(_from, burnAddress, burnTaxValue);
                emit Transfer(_from, globalAddress, globalReward);
            }
        }
    }

    function _transferFrom(
        address _owner,
        address _spender,
        address _to,
        uint256 _valueInWei
    ) private {
        require(_valueInWei > 0, "ERC20: Cannot transfer zero value");
        require(
            _spender != address(0),
            "ERC20: Spender can't be address zero."
        );

        require(
            _allowances[_owner][_spender] >= _valueInWei,
            "ERC20: Insufficient allowance"
        );

        _transfer(_owner, _to, _valueInWei);
        _allowances[_owner][_spender] -= _valueInWei;
    }

    function _getRandomGlobalAddress() private view returns (address) {
        uint256 randomHash = uint256(
            keccak256(
                abi.encodePacked(
                    block.timestamp,
                    block.prevrandao,
                    blockhash(block.number - 1)
                )
            )
        );

        uint256 randomIndex = randomHash % _globalList.length;
        return _globalList[randomIndex];
    }

    function getUserAccount(
        address _userAccount
    ) external view returns (Account memory) {
        return _accounts[_userAccount];
    }

    function adminTax() external view returns (uint8) {
        return _adminTax;
    }

    function adminTaxReceiverAddress() external view returns (address) {
        return _adminTaxReceiver;
    }

    function burnTax() external view returns (uint8) {
        return _burnTax;
    }

    function globalTax() external view returns (uint8) {
        return _globalTax;
    }

    function totalTax() external view returns (uint8) {
        return _adminTax + _globalTax + _burnTax;
    }

    function isExcludedFromTax(address _address) external view returns (bool) {
        return _isExcludedFromTax[_address];
    }

    //exclude wallets from fees, this is needed for launch or other contracts.
    function excludeFromFee(address _address, bool status) external onlyOwner {
        require(
            _isExcludedFromTax[_address] != status,
            "The wallet ialready have that status."
        );
        _isExcludedFromTax[_address] = status;
        emit ExcludeStatus(_address, status);
    }
}