contract asgX is ERC20, BlacklistedRole {

    // name of the token
    string private _name = "asgX";
    // symbol of the token
    string private _symbol = "asgX";
    // decimals of the token
    uint8 private _decimals = 18;

    // initial supply
    uint256 internal constant INITIAL_SUPPLY = 1000000000 * (10 ** 18);

    // Pausable feature
    bool internal _paused;

    // registered contracts (to prevent loss of token via transfer function)
    mapping (address => bool) private _contracts;

    // events
    event Paused(uint256 time);
    event Unpaused(uint256 time);
    event ContractAdded(address contractAddr);
    event ContractRemoved(address contractAddr);

    modifier notPaused {
        require(!paused(), 'Transfers are temporary suspended');
        _;
    }

    /**
      * @dev constructor function that is called once at deployment of the contract.
      * @param recipient Address to receive initial supply.
      */
    constructor(address recipient, address initialOwner) public Ownable(initialOwner) {

        _mint(recipient, INITIAL_SUPPLY);

    }

    /**
      * @dev modified internal transfer function with Pausable and Blacklist features.
      * @param from The address of token holder.
      * @param to The address to transfer to.
      * @param value The amount to be transferred.
      */
    function _transfer(address from, address to, uint256 value) internal notPaused notBlacklisted(from) {

        super._transfer(from, to, value);

    }

    /**
     * @dev modified transfer function that allows to safely send tokens to registered smart contract.
     * @param to The address to transfer to.
     * @param value The amount to be transferred.
     */
    function transfer(address to, uint256 value) public returns (bool) {

        if (_contracts[to]) {
            approveAndCall(to, value, new bytes(0));
        } else {
            super.transfer(to, value);
        }

        return true;

    }

    /**
    * @dev Allows to owner of the contract to suspend any transfer of the token.
    */
    function pause() public onlyOwner {
        require(!paused(), 'Transfers are suspended already');
        _paused = true;
        emit Paused(block.timestamp);
    }

    /**
    * @dev Allows to owner of the contract to revive transfers of the token.
    */
    function unpause() public onlyOwner {
        require(paused(), 'Transfer are not suspended yet');
        _paused = false;
        emit Unpaused(block.timestamp);
    }

    /**
     * @dev Allows to register other smart contracts (to prevent loss of tokens via transfer function).
     * @param addr Address of smart contracts to work with.
     */
    function registerContract(address addr) public onlyOwner {
        require(isContract(addr), 'Address is not a contract');
        _contracts[addr] = true;
        emit ContractAdded(addr);
    }

    /**
     * @dev Allows to unregister registered smart contracts.
     * @param addr Address of smart contracts to work with.
     */
    function unregisterContract(address addr) external onlyOwner {
        require(_contracts[addr]);
        _contracts[addr] = false;
        emit ContractRemoved(addr);
    }

    /**
    * @dev Allows to send tokens (via Approve and TransferFrom) to other smart contract.
    * @param spender Address of smart contracts to work with.
    * @param amount Amount of tokens to send.
    * @param extraData Any extra data.
    */
    function approveAndCall(address spender, uint256 amount, bytes memory extraData) public returns (bool) {
        require(approve(spender, amount));

        ApproveAndCallFallBack(spender).receiveApproval(msg.sender, amount, address(this), extraData);

        return true;
    }

    /**
    * @dev Allows to  owner of the contract withdraw needed ERC20 token from this contract (promo or bounties for example).
    * @param ERC20Token Address of ERC20 token.
    * @param recipient Account to receive tokens.
    */
    function withdrawERC20(address ERC20Token, address recipient) external onlyOwner {

        uint256 amount = IERC20(ERC20Token).balanceOf(address(this));
        require(amount > 0);
        IERC20(ERC20Token).transfer(recipient, amount);

    }

    /**
     * @return true if transfers of token are suspended.
     */
    function paused() public view returns (bool) {
        return _paused;
    }

    /**
     * @return true if the address is registered as contract
     */
    function isRegistered(address addr) public view returns (bool) {
        return _contracts[addr];
    }

    /**
     * @return true if the address is a сontract
     */
    function isContract(address addr) internal view returns (bool) {
        uint size;
        assembly { size := extcodesize(addr) }
        return size > 0;
    }

    /**
     * @return the name of the token.
     */
    function name() public view returns (string memory) {
        return _name;
    }

    /**
     * @return the symbol of the token.
     */
    function symbol() public view returns (string memory) {
        return _symbol;
    }

    /**
     * @return the number of decimals of the token.
     */
    function decimals() public view returns (uint8) {
        return _decimals;
    }

}
/**
 * @title The main contract (g.s).
 */
function approveAndCall(address spender, uint256 amount, bytes memory extraData) public returns (bool) {
    require(approve(spender, amount));

    ApproveAndCallFallBack(spender).receiveApproval(msg.sender, amount, address(this), extraData);

    return true;
}

/**
 * @dev Allows to send tokens (via Approve and TransferFrom) to other smart contract.
 * @param spender Address of smart contracts to work with.
 * @param amount Amount of tokens to send.
 * @param extraData Any extra data.
 */