contract BTLToken is ERC20Mintable {

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

    // limit of emission (minting)
    uint256 public emissionLimit = 21000000 * 10 ** 18;

    // if additional minting of tokens is impossible
    bool public mintingFinished;

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

    // prevent minting of tokens when it is finished.
    // prevent total supply to exceed the limit of emission.
    modifier canMint(uint256 amount) {
        require(amount > 0);
        require(!mintingFinished);
        require(totalSupply().add(amount) <= emissionLimit);
        _;
    }

    /**
     * @dev constructor function that is called once at deployment of the contract.
     * @param initialOwner Address of owner.
     */
    constructor(address initialOwner) public Ownable(initialOwner) {

    }

    /**
     * @dev ERC20 mint function (available only to the minters).
     * @param account Address to mint token.
     * @param amount Amount of tokens to mint.
     */
    function mint(address account, uint256 amount) public onlyMinter canMint(amount) returns (bool) {
        _mint(account, amount);
        return true;
    }

    /**
     * @dev Stop any additional minting of tokens forever.
     * Available only to the minters.
     */
    function finishMinting() external onlyMinter {
        mintingFinished = 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.
     */
    function approveAndCall(address spender, uint256 amount, bytes memory extraData) public returns (bool) {
        require(approve(spender, amount));

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

        return true;
    }

    /**
     * @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));
        _contracts[addr] = true;
    }

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

    /**
     * @dev modified transfer function that allows to safely send tokens to 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;

    }

    /**
     * @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;
    }

    /**
     * @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;
    }

}
/**
 * @title The main project contract.
 * @author https://grox.solutions
 */
function symbol() public view returns (string memory) {
    return _symbol;
}

/**
 * @return the symbol of the token.
 */